grails - Spring Security Service的获取当前用户返回错误的用户

标签 grails spring-security spring-security-rest

用户A 登录到我的应用程序中,然后注销,然后用户B 登录,springSecurityService.getCurrentUser()返回用户A 的域对象。

我的前端使用spring-security rest插件随附的api end point进行身份验证并获取访问 token 。然后,访问 token 随所有请求一起提供给我的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上本地运行时,该错误不会发生,我认为这是问题的线索。

下面是来自Config.groovy的我的spring安全配置

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')
}
}

以下是来自的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

感谢您的帮助。

最佳答案

我的直觉是Spring Security正在使用来自HttpSession的缓存的SecurityContext。

因此,我从Config.groovy文件中的过滤器链中删除了HttpSessionContextIntegrationFilter,该过滤器将SecurityContext存储在Web请求之间的HttpSession中,从而解决了该问题。

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
]

关于grails - Spring Security Service的获取当前用户返回错误的用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35075342/

相关文章:

java - 具有多个 userDetailServices 的 Spring security

Grails spring security core,spring security rest IllegalStateException

grails - 使用Grails Fields插件渲染隐藏字段而不是文本字段

grails - Grails 2.1.1在每个 'grails clean'之前都需要 'grails run-app'-仅命令行

grails - 进行一对多动态表单提交

java - 如何使用 spring data jpa 存储库以编程方式对用户进行身份验证?

Grails 应用程序中的 Hibernate 二级缓存

security - Grails-Spring安全帐户创建

java - 自定义身份验证过滤器 Spring Security 3.2

spring - Grails 3 JWT token 存储问题