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

时间:2015-06-22 09:38:24

标签: spring-security spring-security-oauth2 spring-cloud spring-session

提前感谢您阅读此问题。

设置

我正在使用:

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

并且在单点登录中使用spring-security-oauth2(通过OAuth2ClientContext)时,会对Redis数据存储中spring-session @EnableRedisHttpSession的持久性提出疑问({ {1}}),反向代理(@EnableOAuth2Sso)网关。

问题

在我看来,@EnableZuulProxy中创建的SessionScoped JdkDynamicAopProxied DefaultOAuth2ClientContext未在Redis数据存储中正确保留。

org.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); } } 没有oauth2ClientContext的创建表明(按预期)bean会在每个客户端会话中实例化一次并存储在@EnableRedisHttpSession中。除了在Spring HttpSession的{​​{1}}中存储OAuth2 bearerToken之外,此实例还将重复用于存储提取的OAuth2 accessToken详细信息。

但是,一旦使用SecurityContextorg.springframework.security.core.Authentication bean将首先在会话创建时创建,但也会在稍后创建(同时仍使用相同的客户端会话)。调试Redis客户端会话内容确认会话创建未正确保留@EnableRedisHttpSession

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

bearerToken

我们检索到OAuth2 scopedTarget.oauth2ClientContext后(SpringContext保持不变,但是没有~$ 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" ):

bearerToken

如果我们现在尝试访问其中一个配置程序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" ),将创建另一个Zuul实例(因为Redis没有保留,org.springframework.security.oauth2.client.DefaultOAuth2ClientContext#getAccessToken oauth2ClientContext

有趣的是,此实例稍后将保留在Redis中(但null实例仍然存在,因为AccessToken未被重新询问):

null

创建一个简单的AccessToken注入的bean按预期工作,但是在Redis中正确地保存了bean。

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

实施例

通过添加以下依赖项,可以在@ dave-syer示例中为OAuth2 reverse proxy gateway重现所述问题:

ScopedProxyMode.TARGET_CLASS

以及UiApplication中的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; } } 注释。

问题

我们是否应该忽略自动配置中的<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> 并手动创建具有不同设置的@EnableRedisHttpSession以在Redis中启用org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration持久性?如果是这样,你能举个例子吗?

否则:如何在Redis中保留oauth2ClientContext

提前许多人在阅读这个问题时试图提供帮助。

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

@ dave-syer提示是正确的。

我在此处发布了可用于设置RequestContextFilter并启用spring-session spring-security-oauth@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; } } 对象持久性的配置。如果这可以帮助某人......

[

答案 2 :(得分:1)

我碰到了这篇文章,我遇到了完全相同的问题,但有一些细微的差异:

  • 我的应用程序不是Spring Boot应用程序
  • 我使用JDBC持久性而非Redis

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

“技巧”是在web.xml中定义RequestContextFilter。就我的测试而言,我还没有看到将请求上下文过滤器放在请求上下文侦听器旁边的任何边界影响。

重要的是过滤器的顺序。您需要按以下顺序在web.xml中定义过滤器:

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

类似这样:

<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和其他网站,那将使我很开心。