OAuth2ClientContext (spring-security-oauth2) не сохраняется в Redis при использовании spring-session и spring-cloud-security
большое спасибо за прочтение этого вопроса.
настройка
я использую:
spring-security-oauth2:2.0.7.RELEASEspring-cloud-security:1.0.1.RELEASEspring-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 весной SecurityContext'ы org.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;
}
}