首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >OAuth2ClientContext (Security OAuth2)在使用spring-会话和spring-云安全性时没有在Redis中持久化。

OAuth2ClientContext (Security OAuth2)在使用spring-会话和spring-云安全性时没有在Redis中持久化。
EN

Stack Overflow用户
提问于 2015-06-22 09:38:23
回答 3查看 5.4K关注 0票数 9

谢谢你提前阅读了这个问题。

设置

我正在使用:

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

当在单点登录( spring-security-oauth2 ) (@EnableOAuth2Sso)反向代理(@EnableZuulProxy)网关中使用spring-session (通过@EnableRedisHttpSession)时,会有一个关于在Redis数据存储中使用spring-session(通过@EnableRedisHttpSession)的持久性的问题。

问题

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

代码语言:javascript
运行
复制
@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);
    }

}

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

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

在检索OAuth2 bearerToken (没有SpringContext,没有scopedTarget.oauth2ClientContext)之前:

代码语言:javascript
运行
复制
~$ 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"

在我们检索到OAuth2 bearerToken之后(SpringContext持久化,但没有scopedTarget.oauth2ClientContext):

代码语言:javascript
运行
复制
~$ 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"

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

有趣的是,这个实例稍后将被持久化到Redis中(但是null实例将被持久化,因为AccessToken不会被重新请求):

代码语言:javascript
运行
复制
~$ 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" 

创建一个简单的ScopedProxyMode.TARGET_CLASS注入bean就像预期的那样正常工作,但是在Redis中正确地保存了这个bean。

代码语言:javascript
运行
复制
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;
    }
}

示例

通过添加以下依赖项,可以在@dave示例中再现所描述的OAuth2 reverse proxy gateway问题:

代码语言:javascript
运行
复制
<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>

以及@EnableRedisHttpSessionUiApplication中的注释。

问题

我们是否应该忽略来自org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration的AutoConfiguration,手动创建一个具有不同设置的oauth2ClientContext,以便在Redis中启用spring-session持久性?如果是的话,请你举一个例子好吗?

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

许多人事先向任何阅读这个问题的人试着帮忙。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-06-23 08:28:08

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

票数 5
EN

Stack Overflow用户

发布于 2015-06-29 06:23:01

@dave提示是正确的。

我在这里发布了可用于设置RequestContextFilter和启用spring-security-oauth对象的spring-session持久性的配置。万一能帮上忙..。

代码语言:javascript
运行
复制
@Configuration
public class RequestContextFilterConfiguration {

    @Bean
    @ConditionalOnMissingBean(RequestContextFilter.class)
    public RequestContextFilter requestContextFilter() {
        return new RequestContextFilter();
    }

    @Bean
    public FilterRegistrationBean requestContextFilterChainRegistration(
            @Qualifier("requestContextFilter") Filter securityFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
        registration.setOrder(SessionRepositoryFilter.DEFAULT_ORDER + 1);
        registration.setName("requestContextFilter");
        return registration;
    }
}
票数 4
EN

Stack Overflow用户

发布于 2018-12-07 12:02:39

我偶然发现了这篇文章,我也有同样的问题,但也有一些细微的不同:

  • 我的应用程序不是Spring应用程序
  • 我使用JDBC持久化而不是Redis。

然而,这可能会节省一些时间的未来读者,上述解决方案也对我有效。由于我没有使用Spring,所以我将在这里发布解决方案,以便在使用web.xml配置的非Spring应用程序中应用。

“诀窍”是在web.xml中定义RequestContextFilter。就我的测试而言,我还没有看到让请求上下文过滤器驻留在请求上下文侦听器之外的任何边框效果。

重要的是过滤器的排序。您需要按照这个顺序在web.xml中定义过滤器:

  • 会话存储库筛选器
  • 请求上下文筛选器
  • 安全过滤器

所以,就像:

代码语言:javascript
运行
复制
<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>requestContextFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>requestContextFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

如果这能帮你节省几个小时的时间去挖掘Stackoverflow和其他网站,那就让我很开心了。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30976624

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档