OAuth2ClientContext (spring-security-oauth2) не сохраняется в Redis при использовании spring-session и spring-cloud-security

большое спасибо за прочтение этого вопроса.

настройка

я использую:

  • 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 (via @EnableRedisHttpSession) в единый вход (@EnableOAuth2Sso), обратный прокси (@EnableZuulProxy) шлюза.

кажется мне что то SessionScoped JdkDynamicAopProxied DefaultOAuth2ClientContext создано в org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration неправильно сохраняется в хранилище данных Redis для.

@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 bearerToken детали в дополнение к хранению OAuth2 accessToken весной SecurityContextorg.springframework.security.core.Authentication.

, С помощью @EnableRedisHttpSession, the oauth2ClientContext 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) "lastAccessedTime"
4) "creationTime"
5) "sessionAttr:SPRING_SECURITY_SAVED_REQUEST"

после того, как мы извлекли OAuth2 bearerToken (SpringContext сохраняется, но нет!--29-->):

~$ 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), другой пример oauth2ClientContext будет создан (так как не сохранился в редисе, с собой null AccessToken.

как ни странно, этот экземпляр позже будет сохранен в Redis (но a 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" 

создание простого ScopedProxyMode.TARGET_CLASS Впрыснутый фасоль работал, как ожидалось, однако с фасоль сохраняется правильно в 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;
    }
}

пример

описанная проблема может быть воспроизведена в Примере @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>

а также @EnableRedisHttpSession аннотации UiApplication.

вопрос

должны ли мы игнорировать org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration от Автоконфигурация и вручную создать oauth2ClientContext С другой настройкой для включения spring-session настойчивость в Redis? Если да, то не могли бы вы привести пример?

в противном случае: как упорствовать oauth2ClientContext в Redis?

многие заранее всем, кто читает этот вопрос, пытаются помочь.

2 ответов


там есть известная проблема (https://github.com/spring-projects/spring-session/issues/129 и https://github.com/spring-projects/spring-boot/issues/2637). Вы можете обойти это, добавив RequestContextFilter.


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