使用 spring-session 和 spring-cloud-security 时 OAuth2ClientContext (spring-security-oauth2) 未持久化在 Redis 中 [英] OAuth2ClientContext (spring-security-oauth2) not persisted in Redis when using spring-session and spring-cloud-security

查看:152
本文介绍了使用 spring-session 和 spring-cloud-security 时 OAuth2ClientContext (spring-security-oauth2) 未持久化在 Redis 中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

设置

我正在使用:

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

并且在使用 spring-session(通过@EnableRedisHttpSession) 在单点登录 (@EnableOAuth2Sso)、反向代理 (@EnableZuulProxy) 网关中.

问题

在我看来,在 org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration 中创建的 SessionScoped JdkDynamicAopProxied DefaultOAuth2ClientContext 不是正确保存在 Redis 数据存储中.

@Configuration@ConditionalOnBean(OAuth2SsoConfiguration.class)@ConditionalOnWebApplication受保护的抽象静态类 SessionScopedConfiguration 扩展 BaseConfiguration {@豆@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)公共 OAuth2ClientContext oauth2ClientContext() {返回新的 DefaultOAuth2ClientContext(accessTokenRequest);}}

在没有 @EnableRedisHttpSession 的情况下调试 oauth2ClientContext 的创建表明(如预期的那样)bean 将在每个客户端会话中实例化一次并存储在 HttpSession.除了在 Spring SecurityContextorg 中存储 OAuth2 accessToken 之外,此实例将被重用以存储获取的 OAuth2 bearerToken 详细信息.springframework.security.core.Authentication.

然而,一旦使用 @EnableRedisHttpSessionoauth2ClientContext bean 将首先在会话创建时创建,但稍后也会创建(同时仍然使用相同的客户端会话).调试 Redis 客户端会话内容确认 oauth2ClientContext 没有被会话创建正确持久化:

在我们检索 OAuth2 之前 bearerToken(没有 SpringContext,没有 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)上次访问时间"4)创作时间"5) "sessionAttr:SPRING_SECURITY_SAVED_REQUEST"

在我们检索到 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)上次访问时间"3)创作时间"4) "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"5) "sessionAttr:SPRING_SECURITY_CONTEXT"6) "maxInactiveInterval"

如果我们现在尝试访问配置器 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)创作时间"7)上次访问时间"8)sessionAttr:org.springframework.web.context.request.ServletRequestAttributes.DESTRUCTION_CALLBACK.scopedTarget.oauth2ClientContext"

创建一个简单的 ScopedProxyMode.TARGET_CLASS 注入的 bean 按预期工作,但是 bean 在 Redis 中正确持久化.

 公共类 HelloWorldService 实现了 Serializable {公共 HelloWorldService(){System.out.println("HelloWorldService 已创建");}私人字符串名称=世界";公共字符串getName(){返回名称;}公共无效集名称(字符串名称){this.name=name;}公共字符串 getHelloMessage() {返回你好"+ this.name;}}@配置公共类 AppConfig {private SecureRandom random = new SecureRandom();@豆@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)公共 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("已创建资源HelloWorldService = " + s);返回 s;}}

示例

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

<依赖><groupId>org.springframework.session</groupId><artifactId>spring-session</artifactId><version>1.0.1.RELEASE</version></依赖><依赖><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-redis</artifactId></依赖>

以及UiApplication.

问题

我们是否应该忽略 AutoConfiguration 中的 org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration 并手动创建一个具有不同设置的 oauth2ClientContext 以启用 spring-session 在 Redis 中持久化?如果是这样,你能提供一个例子吗?

否则:如何在 Redis 中持久化 oauth2ClientContext?

对于阅读此问题并试图提供帮助的任何人来说,这是许多提前.

解决方案

那里有一个已知问题 (https://github.com/spring-projects/spring-session/issues/129https://github.com/spring-projects/spring-boot/issues/2637).您可以通过添加 RequestContextFilter 来解决它.

Thanks a lot in advance for reading this question.

Setup

I am using:

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

and would have a question regarding the persistence of spring-security-oauth2 OAuth2ClientContext in a Redis datastore when using spring-session (via @EnableRedisHttpSession) in a Single-Sign-On (@EnableOAuth2Sso), reverse proxy (@EnableZuulProxy) gateway.

Problem

It seems to me that the SessionScoped JdkDynamicAopProxied DefaultOAuth2ClientContext created in org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration is not correctly persisted in the Redis datastore.

@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);
    }

}

Debugging the creation of the oauth2ClientContext without @EnableRedisHttpSession shows that (as expected) the bean will be instantiated once per client session and stored in the HttpSession. This instance will then be reused to store the fetched OAuth2 bearerToken details in addition to storing the OAuth2 accessToken in Spring SecurityContext's org.springframework.security.core.Authentication.

However, once using @EnableRedisHttpSession, the oauth2ClientContext bean will be first created on the session creation but also later on (while still using the same client session). Debugging the Redis client session content confirms that oauth2ClientContext is not correctly being persisted by session creation:

Before we retrieve the 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"

After we retrieved the OAuth2 bearerToken (SpringContext persisted, but NO 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"

If we now try to access one of the configurer Zuul's routes (therefore requiring to call org.springframework.security.oauth2.client.DefaultOAuth2ClientContext#getAccessToken), another instance of oauth2ClientContext will be created (since not persisted in Redis, with a null AccessToken.

Funnily enough, this instance will later be persisted in Redis (but a null instance is persisted since the AccessToken is not re-asked for):

~$ 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" 

Creating a Simple ScopedProxyMode.TARGET_CLASS Injected bean worked as expected however with the bean being persisted correctly in Redis.

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;
    }
}

Example

The described problem can be reproduced in @dave-syer example for an OAuth2 reverse proxy gateway by adding the following dependencies:

<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>

as well as the @EnableRedisHttpSession annotation in UiApplication.

Question

Should we ignore org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration from AutoConfiguration and manually create a oauth2ClientContext with a different setup to enable spring-session persistence in Redis? If so, can you please provide an example?

Otherwise: how to persist oauth2ClientContext in Redis?

Many in advance to anyone reading this question an trying to help.

解决方案

There's a known issue there (https://github.com/spring-projects/spring-session/issues/129 and https://github.com/spring-projects/spring-boot/issues/2637). You can work around it by adding a RequestContextFilter.

这篇关于使用 spring-session 和 spring-cloud-security 时 OAuth2ClientContext (spring-security-oauth2) 未持久化在 Redis 中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆