繁体   English   中英

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

[英]Spring OAuth2 java.util.LinkedHashMap cannot be cast to org.springframework.security.web.authentication.WebAuthenticationDetails

我正在遵循此处的指南其中包含以下代码片段,用于审核来自 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的部分,那么它工作正常。

编辑:所以我刚刚发现我的 details 返回的值与WebAuthenticationDetails的属性不同。 我的详细信息包含 grant_type、范围和用户名,而不是转换为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()");
    }

}

所以, AuthenticationManager是发布认证成功事件的一个,它只被ResourceOwnerPasswordTokenGranter 这就是为什么您只能使用一种授权类型(密码)看到它,因为这是唯一TokenGranter碰巧对资源所有者进行身份验证的TokenGranter

使用剩余的令牌授权,授权服务器会收到一个授权代码、一个刷新令牌,或者只是信任客户端凭据。 由于没有所有者经过身份验证,因此不会发布任何事件。

有争议的是, ResourceOwnerPasswordTokenGranter偶然发布的细节应该是LinkedHashMap以外的东西,但我认为你无论如何都会想做一些不同的事情,因为你更多的是在寻找令牌事件。

对于您想要做的事情,确实没有很好的注入点。 授予令牌的模型与验证用户的模型不同。 例如, TokenEndpoint无权访问 HTTP 请求,而您需要它来构建所需的详细信息对象类型。

你可以做的一件事是扩展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})

不是很好,但它确实为我提供了每个令牌授予的客户端身份验证的审计跟踪。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM