Spring OAuth2 java.util.LinkedHashMap无法强制转换为org.springframework.security.web.authentication.WebAuthenticationDetails

时间:2018-02-28 15:45:49

标签: java spring spring-boot spring-security spring-boot-actuator

我正在关注指南here,该指南包含以下代码片段,用于审核Spring Boot Actuator的Spring Security登录尝试

@Component
public class LoginAttemptsLogger {

    @EventListener
    public void auditEventHappened(
      AuditApplicationEvent auditApplicationEvent) {

        AuditEvent auditEvent = auditApplicationEvent.getAuditEvent();
        System.out.println("Principal " + auditEvent.getPrincipal() 
          + " - " + auditEvent.getType());

        WebAuthenticationDetails details = 
          (WebAuthenticationDetails) auditEvent.getData().get("details");
        System.out.println("Remote IP address: "
          + details.getRemoteAddress());
        System.out.println("  Session Id: " + details.getSessionId());
    }
}

但是当我使用这段代码时,我收到了错误

java.util.LinkedHashMap cannot be cast to org.springframework.security.web.authentication.WebAuthenticationDetails

我正在使用带有Spring Boot Actuator的Spring Boot 1.5.10.RELEASE的无状态OAuth2 JWT安全配置。如果我删除关于details的部分,那么它可以正常工作。

编辑:所以我发现我的详细信息返回的值与WebAuthenticationDetails的属性不同。我的详细信息包含grant_type,scope和username,而不是需要转换为WebAuthenticationDetails的remoteAddress和sessionId。有趣的是,当我访问执行器端点/auditevents时,详细信息字段的值包含remoteAddress和sessionId。嗯。所以这肯定意味着它是因为我使用的是OAuth2,但我不知道究竟是什么原因。

edit2 :我还注意到它只发布了password / client_credentials授权类型的事件。如果可能的话,我也想对refresh_token授权类型使用相同的监听器

edit3 :这是我的授权服务器配置

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Value("${tokenSigningKey}")
    private String tokenSigningKey;

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        CustomJwtAccessTokenConverter accessTokenConverter = new CustomJwtAccessTokenConverter();
        accessTokenConverter.setSigningKey(tokenSigningKey);
        return accessTokenConverter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new CustomJwtJdbcTokenStore(accessTokenConverter(), dataSource);
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomAccessTokenEnhancer();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new CustomPasswordEncoder();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource).passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        endpoints
                .tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.passwordEncoder(passwordEncoder());
        security.checkTokenAccess("isAuthenticated()");
    }

}

1 个答案:

答案 0 :(得分:1)

因此,AuthenticationManager是发布身份验证成功事件的事件,它仅由ResourceOwnerPasswordTokenGranter使用。这就是您只能使用一种授权类型(密码)看到它的原因,因为这是验证资源所有者的唯一TokenGranter

使用剩余的令牌授权,授权服务器将显示授权代码,刷新令牌或仅信任客户端凭据。由于没有所有者通过身份验证,因此不会发布任何事件。

ResourceOwnerPasswordTokenGranter偶然发布的详细信息应该是LinkedHashMap以外的其他内容,但我认为您会想要无论如何都要做一些不同的事情,因为你更需要一个令牌事件。

对于你想要做的事情来说,真的不是一个很好的注入点。授予令牌的模型与用于验证用户的模型不同。例如,TokenEndpoint无法访问HTTP请求,您需要这样才能构建所需的详细信息对象。

你可以做的一件事是icky扩展AuthorizationServerSecurityConfiguration来定制客户端authenticationManager的构建方式。它不是预期的扩展点,但它适用于我:

  1. 扩展AuthorizationServerSecurityConfiguration

    public class PublishingAuthorizationServerSecurityConfiguration
        extends AuthorizationServerSecurityConfiguration {
    
        @Autowired
        AuthenticationEventPublisher authenticationEventPublisher;
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            super.configure(http);
    
            http.getSharedObject(AuthenticationManagerBuilder.class)
                .authenticationEventPublisher
                    (authenticationEventBuilder);
        }
    }
    
  2. 切换@EnableAuthorizationServer
    @Import(
        {AuthorizationServerEndpointsConfiguration.class, 
         PublishingAuthorizationServerSecurityConfiguration.class})
    
  3. 不太好,但它确实为我提供了每个令牌授权的客户端身份验证的审计跟踪。