multiple - spring-boot 1.3.0.RC1: ClassCastException para obtener información de usuario oauth2 de sesión persistió en Redis
spring session multiple sessions (0)
Estoy trabajando con una aplicación de arranque de primavera con inicio de sesión único OAuth2 (en desarrollo, pero ya está funcionando).
y cambié la tienda de sesiones de Tomcat a Redis.
Problema
ClassCastException
cuando intento obtener mi objeto Usuario de SecurityContextHolder
.
Almacenamiento del objeto User en UsernamePasswordAuthenticationToken
@Service
@Transactional
public class MyTokenService implements ResourceServerTokenServices {
@Setter
private OAuth2RestOperations restTemplate;
@Autowired
ResourceServerProperties sso;
@Override
public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException {
// get from http://some-auth-provider/me
MyUser myUser = getMyInfo();
// persist to mysql
UserEntity user = save(myUser);
OAuth2Request request =
new OAuth2Request(null, sso.getClientId(), null, true, null, null, null, null, null);
// org.springframework.boot.devtools.restart.classloader.RestartClassLoader@3d66ad6e
logger.info(user.getClass().getClassLoader().toString());
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(user.getId(), "N/A", user.getAuthorities());
token.setDetails(user);
return new OAuth2Authentication(request, token);
}
@Override
public OAuth2AccessToken readAccessToken(String accessToken) {
throw new UnsupportedOperationException("Not supported: read access token");
}
}
Sesión después de autenticada
127.0.0.1:6379> keys *
1) "spring:session:expirations:1445309400000"
2) "spring:session:sessions:3447d160-5f41-49c9-abf9-2bf0ef2e6bc2"
127.0.0.1:6379> hkeys spring:session:sessions:3447d160-5f41-49c9-abf9-2bf0ef2e6bc2
1) "sessionAttr:SPRING_SECURITY_SAVED_REQUEST"
2) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
3) "lastAccessedTime"
4) "maxInactiveInterval"
5) "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"
6) "sessionAttr:SPRING_SECURITY_CONTEXT"
7) "creationTime"
127.0.0.1:6379> type spring:session:sessions:3447d160-5f41-49c9-abf9-2bf0ef2e6bc2:sessionAttr:SPRING_SECURITY_CONTEXT
none
Retirando el objeto de usuario (después de autenticado)
public UserEntity getMyInfo() {
OAuth2Authentication oAuth2Authentication =
(OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
Long userId = (Long) oAuth2Authentication.getPrincipal();
UsernamePasswordAuthenticationToken userAuthentication =
(UsernamePasswordAuthenticationToken) oAuth2Authentication.getUserAuthentication();
try {
// sun.misc.Launcher$AppClassLoader@14dad5dc
logger.info(userAuthentication.getDetails().getClass().getClassLoader().toString());
return (UserEntity) userAuthentication.getDetails();
} catch (Exception e) {
// fixme ClassCastException
// workaround
return userRepository.accountDetail(userId);
}
}
userAuthentication
objeto userAuthentication
y confirmo userAuthentication.details
con el objeto MyUser. entonces ClassCastException
ocurre al convertir UserEntity a UserEntity, pero ¿por qué?
Puedo obtener el principal (= UserEntity.id) correctamente.
¿Alguien tiene alguna solución?
a continuación es parte de la configuración actual.
Dependencias
springBootVersion = ''1.3.0.RC1''
compile("org.springframework.boot:spring-boot-starter-redis")
compile("org.springframework.session:spring-session")
compile("com.github.kstyrc:embedded-redis:0.6")
Configuración de Redis
@Configuration
@EnableRedisHttpSession
public class RedisConfig {
private static RedisServer redisServer;
@Profile({"local", "unit"})
@Bean
public RedisConnectionFactory connectionFactory() throws IOException {
RedisExecProvider customProvider = RedisExecProvider.defaultProvider()
.override(OS.MAC_OS_X, Architecture.x86_64, "/path/to/redis/3.0.2/redis-server");
redisServer = new RedisServerBuilder()
.redisExecProvider(customProvider)
.port(Protocol.DEFAULT_PORT)
.build();
redisServer.start();
return new JedisConnectionFactory();
}
@Profile({"local", "unit"})
@PreDestroy
public void destroy() {
if (redisServer != null) {
redisServer.stop();
}
}
@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;
}
Configuración de OAuth2 Sso
@Configuration
@EnableOAuth2Sso
@EnableWebSecurity
public class OAuth2Config extends WebSecurityConfigurerAdapter {
@Autowired
ResourceServerProperties sso;
@Autowired
@Qualifier("userInfoRestTemplate")
private OAuth2RestOperations restTemplate;
@Primary
@Bean
public ResourceServerTokenServices userInfoTokenServices() {
MyTokenService tokenService = new MyTokenService();
tokenService.setRestTemplate(restTemplate);
return tokenService;
}
// ...
}
Problema similar
Cambio el orden de filtro (en RedisConfig.java) como respuesta anterior, pero no funciona.
Mi orden de filtro actual está debajo.
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''metricFilter'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''springSessionRepositoryFilter'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''requestContextFilter'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''characterEncodingFilter'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''OAuth2ClientContextFilter'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''springSecurityFilterChain'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''webRequestLoggingFilter'' to: [/*]
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ''applicationContextIdFilter'' to: [/*]
Código de actualización
la causa de ClassCastException es la discrepancia de ClassLoader.
org.springframework.boot.devtools.restart.classloader.RestartClassLoader
y sun.misc.Launcher$AppClassLoader
pero no tengo idea de resolver este problema, como siempre.
Resuelto
Cambio el orden de filtro a continuación y funciona.
- requestContextFilter
- oAuth2ClientContextFilter
- springSessionRepositoryFilter