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
весной 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;
}
}