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

标签 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 1.5.10.RELEASE 和 Spring Boot Actuator 来使用无状态 OAuth2 JWT 安全配置。如果我删除有关详细信息的部分,那么它就可以正常工作。

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

edit2:我还注意到它仅发布密码/client_credentials 授予类型的事件。如果可能的话,我也想对刷新 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。

对于剩余的 token 授予,授权服务器会收到授权代码、刷新 token ,或者仅信任客户端凭据。由于没有所有者经过身份验证,因此不会发布任何事件。

有争议的是,ResourceOwnerPasswordTokenGranter 附带发布的详细信息应该是 LinkedHashMap 之外的其他内容,但我认为您会想要做一些事情无论如何,不​​同,因为您更多地寻找 token 事件。

对于您想要做的事情来说,并没有真正好的注入(inject)点。授予 token 的模型与验证用户身份的模型不同。例如,TokenEndpoint 无法访问 HTTP 请求,而您需要访问 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})
    

不太好,但它确实为我提供了每个 token 授予的客户端身份验证的审核跟踪。

关于java - Spring OAuth2 java.util.LinkedHashMap 无法转换为 org.springframework.security.web.authentication.WebAuthenticationDetails,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49033409/

相关文章:

java - 我如何将异常从 servlet 过滤器传递到 Spring 组件

java - Spring 启动: Iterate and evaluate all (active) sessions

spring-boot - Spring Boot Liquibase 版本化数据库迁移 - 处理不同环境、数据库供应商和数据库凭证

spring - 在Spring Boot中结合来自Elasticsearch和PostgreSQL的两个API

java - 在数据库和 postman 上无法看到实际上传的文件(.pdf 格式)格式?

java - 使用 "pure"String 和从方法返回的 String 有什么区别?

java - 如何在未使用 XML 配置文件配置的 Spring 应用程序中激活 JSR-250 注释?

Spring Data Rest - PUT 不适用于关联的引用类型?

java - @CollectionTable 和 @ElementCollection 用法

java - 当单元格包含空值时从excel中获取数据