当用户A 登录我的应用程序,然后注销,然后用户B 登录,springSecurityService.getCurrentUser()
返回用户A 域对象。
我的前端使用spring-security rest插件附带的api end point进行身份验证并获取访问令牌。然后向访问令牌提供对我的api的所有请求。
据我所知,此错误仅影响一个终点。将带有请求的相同access_token传递给有问题的终点并且正常工作会导致他们各自调用getCurrentUser()
来回复两个不同的用户。
我没有使用静态或非静态的任何类成员。
我在我的grails(2.4.4)应用程序中使用spring-security-rest(1.5.3)和spring-security-core(2.0.0)进行身份验证。
应用程序在Tomcat 7(Java 7)服务器上远程运行。当应用程序在我的Mac上本地运行时,不会发生该错误,我认为这是该问题的线索。
以下是我的Spring安全配置来自Config.groovy
grails.plugin.springsecurity.interceptUrlMap = [
'/assets/**': ['permitAll'],
'/**/js/**': ['permitAll'],
'/**/css/**': ['permitAll'],
'/**/images/**': ['permitAll'],
'/**/favicon.ico': ['permitAll'],
'/login/**': ['permitAll'],
'/blank/**': ['permitAll'],
'/register/**': ['permitAll'],
'/api/v1/signup': ['permitAll'],
'/api/v1/register': ['permitAll'],
'/api/v1/approved_emails': ['permitAll'],
'/api/v1/request_password_reset': ['permitAll'],
'/api/v1/reset_password': ['permitAll'],
'/oauth/**': ['permitAll'],
'/': ['isAuthenticated()'],
'/index': ['isAuthenticated()'],
'/index.gsp': ['isAuthenticated()'],
'/**': ['isAuthenticated()']
]
grails.plugin.springsecurity.auth.loginFormUrl = "/pages/index"
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = "/pages/index"
grails.plugin.springsecurity.defaultTargetUrl = "/"
grails.plugin.springsecurity.sch.strategyName = org.springframework.security.core.context.SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
grails.plugin.springsecurity.rest.login.endpointUrl = '/api/v1/login'
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // Stateless chain
'/oauth/**': 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // Stateless chain
'/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter' // Traditional chain
]
以下是来自UrlMappings.groovy
的网址映射class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/api/v1/document"(resources: "documentRest", includes:['show','update']){
"/history"(resources: "documentHistoryRest", includes: ['index'])
}
"/api/v1/response"(resources: "responseRest", includes:['save', 'show', 'update']) {
"/candidate_answers"(resources: "messagesRest", includes: ['show']) {
"/source_document"(resources: "postRest", includes: ['index'])
"/feedback"(resources: "candidateFeedbackRest", includes: ['save'])
}
}
"/api/v1/result"(resources: "resultRest", includes:['index','update', 'save'])
"/api/v1/user"(resources: "userRest", includes:['index']) {
"/memo"(resources: "userMemoRest", includes: ['index','save', 'show','update'])
"/folders"(resources: "userfoldersRest", includes:['index','save', 'show','update','delete']){
"/items"(resources: "userfolderItemsRest", includes: ['index','show','save','delete'])
}
}
'/api/v1/location_search'(resources: 'locationSearchRest', includes: ['index'])
"/api/v1/request_password_reset"(resources: "requestPasswordResetRest", includes: ['save'])
"/api/v1/reset_password"(resources: "resetPasswordRest", includes: ['save'])
"/api/v1/register"(resources: "registerRest", includes:['save'])
"/api/v1/approved_emails"(resources: "approvedEmailsRest", includes: ['index'])
"/api/v1/autocomplete"(resources: "autoCompleteRest", includes:['index'])
"500"(view:'/error')
}
}
以下是来自的debug信息:
'/api/v1/response'; against '/api/**'
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 1 of 7 in additional filter chain; firing Filter: 'SecurityRequestHolderFilter'
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 2 of 7 in additional filter chain; firing Filter: 'MutableLogoutFilter'
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 3 of 7 in additional filter chain; firing Filter: 'RestAuthenticationFilter'
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG rest.RestAuthenticationFilter - Actual URI is /api/v1/response; endpoint URL is /api/v1/login
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 4 of 7 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 5 of 7 in additional filter chain; firing Filter: 'RestTokenValidationFilter'
2016-01-29 18:03:28,583 [http-bio-8080-exec-5] DEBUG bearer.BearerTokenReader - Looking for bearer token in Authorization header, query string or Form-Encoded body parameter
2016-01-29 18:03:28,584 [http-bio-8080-exec-5] DEBUG bearer.BearerTokenReader - Found bearer token in Authorization header
2016-01-29 18:03:28,584 [http-bio-8080-exec-5] DEBUG rest.RestTokenValidationFilter - Token found: xxxxxxxxxxxxxxxxxxxxxx
2016-01-29 18:03:28,584 [http-bio-8080-exec-5] DEBUG rest.RestTokenValidationFilter - Trying to authenticate the token
2016-01-29 18:03:28,584 [http-bio-8080-exec-5] DEBUG rest.RestAuthenticationProvider - Trying to validate token xxxxxxxxxxxxxxxxxxxxxx
2016-01-29 18:03:28,584 [http-bio-8080-exec-5] DEBUG rest.JwtService - Parsed an HMAC signed JWT
2016-01-29 18:03:28,585 [http-bio-8080-exec-5] DEBUG jwt.JwtTokenStorageService - Successfully verified JWT
2016-01-29 18:03:28,585 [http-bio-8080-exec-5] DEBUG jwt.JwtTokenStorageService - Trying to deserialize the principal object
2016-01-29 18:03:28,587 [http-bio-8080-exec-5] DEBUG jwt.JwtTokenStorageService - UserDetails deserialized: grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES
2016-01-29 18:03:28,587 [http-bio-8080-exec-5] DEBUG rest.JwtService - Parsed an HMAC signed JWT
2016-01-29 18:03:28,588 [http-bio-8080-exec-5] DEBUG rest.RestAuthenticationProvider - Now is Fri Jan 29 18:03:28 UTC 2016 and token expires at Fri Jan 29 19:01:47 UTC 2016
2016-01-29 18:03:28,588 [http-bio-8080-exec-5] DEBUG rest.RestAuthenticationProvider - Expiration: 3498
2016-01-29 18:03:28,588 [http-bio-8080-exec-5] DEBUG rest.RestAuthenticationProvider - Authentication result: grails.plugin.springsecurity.rest.token.AccessToken(accessToken:xxxxxxxxxxxxxxxxxxxxxx, expiration:3498, refreshToken:null, principal:grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES, super:grails.plugin.springsecurity.rest.token.AccessToken@77be21e4: Principal: grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_NO_ROLES)
2016-01-29 18:03:28,589 [http-bio-8080-exec-5] DEBUG rest.RestTokenValidationFilter - Token authenticated. Storing the authentication result in the security context
2016-01-29 18:03:28,589 [http-bio-8080-exec-5] DEBUG rest.RestTokenValidationFilter - Authentication result: grails.plugin.springsecurity.rest.token.AccessToken(accessToken:xxxxxxxxxxxxxxxxxxxxxx, expiration:3498, refreshToken:null, principal:grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES, super:grails.plugin.springsecurity.rest.token.AccessToken@77be21e4: Principal: grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_NO_ROLES)
2016-01-29 18:03:28,589 [http-bio-8080-exec-5] DEBUG rest.RestTokenValidationFilter - Continuing the filter chain
2016-01-29 18:03:28,589 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 6 of 7 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2016-01-29 18:03:28,589 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response at position 7 of 7 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2016-01-29 18:03:28,590 [http-bio-8080-exec-5] DEBUG intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /api/v1/response; Attributes: [isAuthenticated()]
2016-01-29 18:03:28,590 [http-bio-8080-exec-5] DEBUG intercept.FilterSecurityInterceptor - Previously Authenticated: grails.plugin.springsecurity.rest.token.AccessToken(accessToken:xxxxxxxxxxxxxxxxxxxxxx, expiration:3498, refreshToken:null, principal:grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES, super:grails.plugin.springsecurity.rest.token.AccessToken@77be21e4: Principal: grails.plugin.springsecurity.userdetails.GrailsUser@ff0d1268: Username: userb@email.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_NO_ROLES; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_NO_ROLES)
2016-01-29 18:03:28,590 [http-bio-8080-exec-5] DEBUG hierarchicalroles.RoleHierarchyImpl - getReachableGrantedAuthorities() - From the roles [ROLE_NO_ROLES] one can reach [ROLE_NO_ROLES] in zero or more steps.
2016-01-29 18:03:28,590 [http-bio-8080-exec-5] DEBUG intercept.FilterSecurityInterceptor - Authorization successful
2016-01-29 18:03:28,590 [http-bio-8080-exec-5] DEBUG intercept.FilterSecurityInterceptor - RunAsManager did not change Authentication object
2016-01-29 18:03:28,590 [http-bio-8080-exec-5] DEBUG web.FilterChainProxy - /api/v1/response reached end of additional filter chain; proceeding with original chain
2016-01-29 18:03:35,334 [http-bio-8080-exec-5] DEBUG access.ExceptionTranslationFilter - Chain processed normally
提前感谢您的帮助。
答案 0 :(得分:1)
我的预感是Spring Security正在使用来自HttpSession的缓存SecurityContext。
所以我删除了HttpSessionContextIntegrationFilter,它在我的Config.groovy文件中的过滤器链中,在Web请求之间的HttpSession中存储了SecurityContext,它解决了问题。
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter, -httpSessionContextIntegrationFilter', // Stateless chain
'/oauth/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // Stateless chain
'/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter' // Traditional chain
]