Невозможно подключиться к контроллеру Grails v3 rest, получить 403, доступ запрещен
Процесс.
создал новое приложение Grails v3 и добавил следующие зависимости в build.gradle
compile group: 'org.springframework.security', name: 'spring-security-core', version: '4.2.1.RELEASE'
compile 'org.grails.plugins:spring-security-core:3.1.1'
compile "org.grails.plugins:spring-security-rest:2.0.0.M2"
создал конфигурацию User Role Group, используя быстрый запуск безопасности grails, и настроил пользователя, некоторые роли и несколько сообщений для пользователей в bootstrap.groovy. посмотрел в / dbconsole и записи сохранены. мои базовые интеграционные тесты кажутся работой
создал контроллер отдыха, как это - я ничего не защищаю в этой точке
import grails.rest.RestfulController
class PostRestController extends RestfulController {
static responseFormats = ["json", "xml"]
//constructor - tells rest controller which domain class to scaffold
PostRestController() {
super (Post)
}
}
после большого чтения в сети и т. д. я закончил с этим в моем application.groovy
//added to avoid login screens for dbconsole. if you add /** at end it works
grails.plugin.springsecurity.rejectIfNoRule = true//false//
grails.plugin.springsecurity.fii.rejectPublicInvocations = false
grails.plugin.springsecurity.securityConfigType = "Annotation"
grails.plugin.springsecurity.useSecurityEventListener = true //enable security events
//Rest
grails.plugin.springsecurity.useBasicAuth = true
grails.plugin.springsecurity.basic.realName = "coffeeShopApp"
grails.plugin.springsecurity.rest.login.useJsonCredentials = true //default
grails.plugin.springsecurity.rest.login.active = true //default
grails.plugin.springsecurity.rest.login.endpointUrl = '/api/login'
grails.plugin.springsecurity.rest.login.failureStatusCode = 401
grails.plugin.springsecurity.rest.token.validation.enableAnonymousAccess = true
// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'org.softwood.security.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'org.softwood.security.UserToUserGroup'
grails.plugin.springsecurity.authority.className = 'org.softwood.security.Role'
grails.plugin.springsecurity.authority.groupAuthorityNameField = 'authorities'
grails.plugin.springsecurity.useRoleGroups = true
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
[pattern: '/', access: ['permitAll']],
[pattern: '/error', access: ['permitAll']],
[pattern: '/index', access: ['permitAll']],
[pattern: '/index.gsp', access: ['permitAll']],
[pattern: '/shutdown', access: ['permitAll']],
[pattern: '/dbconsole/**', access: ['permitAll']], //added
[pattern: '/console/**', access: ['permitAll']], //added
[pattern: '/secureTest/secure', access: ['ROLE_ADMIN']],
[pattern: '/secureTest/index', access: ['permitAll']],
[pattern: '/secureTest/willsPage', access: ["authentication.name == 'will'"]],
[pattern: '/api/**', access: ['permitAll']],
[pattern: '/assets/**', access: ['permitAll']],
[pattern: '/**/js/**', access: ['permitAll']],
[pattern: '/**/css/**', access: ['permitAll']],
[pattern: '/**/images/**', access: ['permitAll']],
[pattern: '/**/favicon.ico', access: ['permitAll']]
]
grails.plugin.springsecurity.filterChain.chainMap = [
[pattern: '/assets/**', filters: 'none'],
[pattern: '/**/js/**', filters: 'none'],
[pattern: '/**/css/**', filters: 'none'],
[pattern: '/**/images/**', filters: 'none'],
[pattern: '/**/favicon.ico', filters: 'none'],
[pattern: '/api/guest/**', filters: 'anonymousAuthenticationFilter,' +
'restTokenValidationFilter,' +
'restExceptionTranslationFilter,' +
'filterInvocationInterceptor'],
[pattern: '/api/**', filters: 'JOINED_FILTERS,' +
'-anonymousAuthenticationFilter,' +
'-exceptionTranslationFilter,' +
'-authenticationProcessingFilter,' +
'-securityContextPersistenceFilter,' +
'-rememberMeAuthenticationFilter'],
[pattern: '/**', filters: 'JOINED_FILTERS,' +
//'-basicAuthenticationFeature,' +
//'-basicExceptionTranslationFilter,' +
'-restTokenValidationFilter,' +
'-restExceptionTranslationFilter']
]
мои статические правила имеют /api/**, как разрешают все. я обновил карту цепи для /api/** в соответствии с остальной документацией безопасности безопасности
я обновил свои UrlMappings следующим образом
class UrlMappings {
static mappings = {
//add alternate url mapping for rest based resources
"/api/posts" (resources : "postRest")
"/api/guest/posts" (resources : "postRest")
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
когда я запускаю отчет url-mappings-report в консоли grails, мои URL-адреса выглядят правильно
Я запускаю приложение - все идет нормально
Затем я использую Chrome для публикации в api / login, когда я использую Chrome Rest Client для подключения - это дает мне 200 и access_token
Я пытаюсь сделать сообщение проверить с возвращенным ключом и получить HTML 200 для того, что выглядит внешний вид
наконец я пытаюсь позвонить в мой / api / posts сервис. я вырезаю и вставляю свой ключ и добавляю в заголовок GET и отправляю, и я получаю 403 / запрещено
Я не вижу, что у меня не так в конфиге, и почти потерял волю к тому, чтобы жить сегодня. может кто-нибудь увидеть - указать, где мой конфиг не работает.
не могу увидеть, где я ошибся - я надеюсь, что свежая пара глаз может помочь увидеть, у меня есть это в журнале трассировки, относящейся к последним / api / guest / posts - это может помочь
2017-03-06 22:32:05.239 DEBUG --- [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /api/guest/posts; Attributes: [permitAll]
2017-03-06 22:32:05.239 DEBUG --- [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: grails.plugin.springsecurity.authentication.GrailsAnonymousAuthenticationToken@f23b6234: Principal: org.springframework.security.core.userdetails.User@dc730200: Username: __grails.anonymous.user__; Password: [PROTECTED]; Enabled: false; AccountNonExpired: false; credentialsNonExpired: false; AccountNonLocked: false; Granted Authorities: ROLE_ANONYMOUS; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@ffffc434: RemoteIpAddress: 192.168.1.238; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2017-03-06 22:32:05.239 DEBUG --- [nio-8080-exec-5] o.s.s.a.h.RoleHierarchyImpl : getReachableGrantedAuthorities() - From the roles [ROLE_ANONYMOUS] one can reach [ROLE_ANONYMOUS] in zero or more steps.
2017-03-06 22:32:05.250 DEBUG --- [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
2017-03-06 22:32:05.250 DEBUG --- [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
2017-03-06 22:32:05.250 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /api/guest/posts reached end of additional filter chain; proceeding with original chain
2017-03-06 22:32:05.277 DEBUG --- [nio-8080-exec-5] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/assets/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/**/js/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/**/css/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/**/images/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/**/favicon.ico'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/api/guest/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/api/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Request '/error' matched by universal pattern '/**'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 1 of 12 in additional filter chain; firing Filter: 'SecurityRequestHolderFilter'
2017-03-06 22:32:05.288 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 3 of 12 in additional filter chain; firing Filter: 'MutableLogoutFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/error'; against '/logoff'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 4 of 12 in additional filter chain; firing Filter: 'GrailsUsernamePasswordAuthenticationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 5 of 12 in additional filter chain; firing Filter: 'RestAuthenticationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 6 of 12 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 7 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 8 of 12 in additional filter chain; firing Filter: 'GrailsRememberMeAuthenticationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 9 of 12 in additional filter chain; firing Filter: 'GrailsAnonymousAuthenticationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 10 of 12 in additional filter chain; firing Filter: 'UpdateRequestContextHolderExceptionTranslationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 11 of 12 in additional filter chain; firing Filter: 'UpdateRequestContextHolderExceptionTranslationFilter'
2017-03-06 22:32:05.289 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2017-03-06 22:32:05.293 DEBUG --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /error reached end of additional filter chain; proceeding with original chain
2017-03-06 22:32:07.314 DEBUG --- [nio-8080-exec-5] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2017-03-06 22:32:07.315 DEBUG --- [nio-8080-exec-5] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
наконец - я включил fii.rejectPublicInvocations и попробовал снова, и я получаю эту трассировку стека, когда я получаю доступ к URL
java.lang.IllegalArgumentException: Secure object invocation FilterInvocation: URL: /api/posts was denied as public invocations are not allowed via this interceptor. This indicates a configuration error because the rejectPublicInvocations property is set to 'true'
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:201)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at grails.plugin.springsecurity.web.UpdateRequestContextHolderExceptionTranslationFilter.doFilter(UpdateRequestContextHolderExceptionTranslationFilter.groovy:64)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
сейчас все отчаянно, вся помощь оценивается в этот момент
NB - см. Комментарий ниже - я сделал "взлом" PostRestController, как это - и заставил результат "true". Теперь это позволяет мне получить доступ к api/guest/posts (анонимно) и / api / posts с ключом входа
@Secured (closure = {
assert request
assert ctx
println """details passed to rest controller >
authentication name : ${authentication.name}
is authenticed : ${isAuthenticated()}
has any role from admin or user: ${hasAnyRole('ROLE_ADMIN','ROLE_USER')}
has role admin : ${hasRole('ROLE_ADMIN')}
principal : $principal
"""
true
} )
class PostRestController extends RestfulController {
static responseFormats = ["json", "xml"]
//constructor - tells rest controller which domain class to scaffold
PostRestController() {
super (Post)
}
}
я включил фильтр отладки, который выводит это, когда я получаю доступ к API / сообщения
Request received for '/api/posts':
SecurityContextHolderAwareRequestWrapper[ FirewalledRequest[ org.apache.catalina.connector.RequestFacade@3085690a]]
servletPath:/api/posts
pathInfo:null
Security filter chain: [
SecurityRequestHolderFilter
MutableLogoutFilter
RestAuthenticationFilter
SecurityContextHolderAwareRequestFilter
RestTokenValidationFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
и мой след записи в консоль от закрытия дал мне это
details passed to rest controller >
authentication name : will
is authenticed : true
has any role from admin or user: true
has role admin : true
principal : grails.plugin.springsecurity.userdetails.GrailsUser@37afd2: Username: will; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER