使用spring-session和spring-cloud-security时,在Redis中不保留OAuth2ClientContext(spring-security-oauth2)

Jér*_*mie 9 spring-security spring-security-oauth2 spring-session spring-cloud

非常感谢您提前阅读这个问题.

建立

我在用:

  • spring-security-oauth2:2.0.7.RELEASE
  • spring-cloud-security:1.0.1.RELEASE
  • spring-session:1.0.1.RELEASE

并且在单一登录(),反向代理()网关中spring-security-oauth2 OAuth2ClientContext使用spring-session(via @EnableRedisHttpSession)时,会有一个关于Redis数据存储区中持久性的问题.@EnableOAuth2Sso@EnableZuulProxy

问题

在我看来,创建的SessionScopedJdkDynamicAopProxied 未正确保存在Redis数据存储区中.DefaultOAuth2ClientContextorg.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration

@Configuration
@ConditionalOnBean(OAuth2SsoConfiguration.class)
@ConditionalOnWebApplication
protected abstract static class SessionScopedConfiguration extends BaseConfiguration {

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
    public OAuth2ClientContext oauth2ClientContext() {
        return new DefaultOAuth2ClientContext(accessTokenRequest);
    }

}
Run Code Online (Sandbox Code Playgroud)

调试oauth2ClientContextwithout 的创建@EnableRedisHttpSession表明(如预期的那样)bean将在每个客户端会话中实例化一次并存储在HttpSession.然后,这个实例将被重用存储获取的OAuth2 bearerToken除了存储的OAuth2细节accessToken在春天SecurityContextorg.springframework.security.core.Authentication.

但是,一旦使用@EnableRedisHttpSession,oauth2ClientContextbean将首先在会话创建时创建,但也将在稍后创建(同时仍使用相同的客户端会话).调试Redis客户端会话内容可确认oauth2ClientContext会话创建未正确保留:

在我们检索OAuth2之前bearerToken(NO SpringContext,NO scopedTarget.oauth2ClientContext):

~$ redis-cli hkeys "spring:session:sessions:17c5e80b-390c-4fd6-b5f9-a6f225dbe8ea"
1) "maxInactiveInterval"
2) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
3) "lastAccessedTime"
4) "creationTime"
5) "sessionAttr:SPRING_SECURITY_SAVED_REQUEST"
Run Code Online (Sandbox Code Playgroud)

在我们检索到OAuth2之后bearerToken(SpringContext保持不变,但是没有scopedTarget.oauth2ClientContext):

~$ redis-cli hkeys "spring:session:sessions:844ca2c4-ef2f-43eb-b867-ca6b88025c8b"
1) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
2) "lastAccessedTime"
3) "creationTime"
4) "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"
5) "sessionAttr:SPRING_SECURITY_CONTEXT"
6) "maxInactiveInterval"
Run Code Online (Sandbox Code Playgroud)

如果我们现在尝试访问其中一个配置器Zuul的路由(因此需要调用org.springframework.security.oauth2.client.DefaultOAuth2ClientContext#getAccessToken),将创建另一个实例oauth2ClientContext(因为没有在Redis中持久存在,带有null AccessToken.

有趣的是,这个实例稍后将被保存在Redis中(但是null由于AccessToken没有重新请求,因此实例仍然存在):

~$ redis-cli hkeys "spring:session:sessions:c7120835-6709-4c03-8d2c-98f830ed6104"
1) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
2) "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"
3) "sessionAttr:scopedTarget.oauth2ClientContext"
4) "sessionAttr:SPRING_SECURITY_CONTEXT"
5) "maxInactiveInterval"
6) "creationTime"
7) "lastAccessedTime"
8) "sessionAttr:org.springframework.web.context.request.ServletRequestAttributes.DESTRUCTION_CALLBACK.scopedTarget.oauth2ClientContext" 
Run Code Online (Sandbox Code Playgroud)

创建一个简单的ScopedProxyMode.TARGET_CLASSInjected bean按预期工作,但是在Redis中正确地保存了bean.

public class HelloWorldService implements Serializable {

    public HelloWorldService(){
        System.out.println("HelloWorldService created");
    }

    private String name = "World";

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name=name;
    }

    public String getHelloMessage() {
        return "Hello " + this.name;
    }
}

@Configuration
public class AppConfig {

    private SecureRandom random = new SecureRandom();

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public HelloWorldService myHelloService(){
        HelloWorldService s = new HelloWorldService();
        String name = new BigInteger(130, random).toString(32);
        System.out.println("name = " + name);
        s.setName(name);
        System.out.println("Resource HelloWorldService created = " + s);
        return s;
    }
}
Run Code Online (Sandbox Code Playgroud)

通过添加以下依赖项,可以在@ dave-syer示例中为OAuth2反向代理网关重现所述问题:

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session</artifactId>
  <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-redis</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

以及UiApplication中@EnableRedisHttpSession注释.

我们应该忽略org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration AutoConfiguration并手动创建oauth2ClientContext一个不同的设置来启用spring-sessionRedis中的持久性吗?如果是这样,你能举个例子吗?

否则:如何坚持oauth2ClientContextRedis?

许多人提前阅读这个问题并尝试提供帮助.