spring-boot 1.3.0.RC1:从Redis中保留会话中的oauth2用户信息时出现ClassCastException

时间:2015-10-20 06:16:07

标签: spring redis spring-boot spring-security-oauth2 spring-session

我正在使用具有OAuth2单点登录功能的spring-boot应用程序 (在开发中,但已经开始工作)。

我将会话存储从Tomcat更改为Redis。

问题

当我尝试从ClassCastException获取用户对象时,我得到SecurityContextHolder

将用户对象存储到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");
  }
}

经过身份验证后的会话

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

Retreiving User对象(经过身份验证后)

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对象并确认userAuthentication.details有MyUser对象。 所以在将UserEntity转换为UserEntity时出现ClassCastException,但为什么?

我可以正确获得主体(= UserEntity.id)。

有没有人有任何解决方案?

下面的

是当前配置的一部分。

依赖关系

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")

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

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

  // ...
}

类似问题

OAuth2ClientContext (spring-security-oauth2) not persisted in Redis when using spring-session and spring-cloud-security

我按照上面的答案更改过滤顺序(在RedisConfig.java中), 但它不起作用。

我当前的过滤顺序如下。

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: [/*]

更新代码

ClassCastException的原因是ClassLoader的不匹配。

org.springframework.boot.devtools.restart.classloader.RestartClassLoadersun.misc.Launcher$AppClassLoader

但我不知道如何解决这个问题。

解决!

我在下面更改了过滤器顺序,但它确实有效。

  1. RequestContextFilter两个
  2. oAuth2ClientContextFilter
  3. springSessionRepositoryFilter

0 个答案:

没有答案