ユーザー Aがアプリケーションにログインしてからログアウトすると、ユーザー Bがログインし、ユーザー Aのドメイン オブジェクトspringSecurityService.getCurrentUser()
が返されます。
私のフロント エンドは、spring-security rest プラグインに付属するAPI エンドポイントを使用して、アクセス トークンを認証して取得します。その後、API へのすべてのリクエストでアクセス トークンが提供されます。
私の知る限り、このバグは 1 つのエンドポイントにのみ影響します。問題のあるエンドポイントと機能するエンドポイントへのリクエストで同じ access_token を渡すと、それぞれの呼び出しがgetCurrentUser()
2 人の異なるユーザーに返されます。
静的または非静的なクラス メンバーを使用していません。
grails (2.4.4) アプリケーションでの認証に spring-security-rest (1.5.3) と spring-security-core (2.0.0) を使用しています。
アプリケーションは、Tomcat 7 (Java 7) サーバー上でリモートで実行されています。アプリケーションが Mac でローカルに実行されている場合、バグは発生しません。これが問題の手がかりになると思います。
以下は、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 からの私の URL マッピングです。
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')
}
}
以下は、次のデバッグ情報です。
'/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
事前にご協力いただきありがとうございます。