oauth-2.0 如何在BearerTokenAuthenticationFilter上设置认证失败处理程序?

krcsximq  于 2022-10-31  发布在  其他
关注(0)|答案(1)|浏览(266)

我可以使用http.oauth2ResourceServer().jwt()创建过滤器链,并且我也设置了spring.security.oauth2.resourceserver.jwt.issuer-uri。它可以对请求进行身份验证。但是,我还需要在身份验证失败的情况下进行自定义日志记录。我采用的方法是使用自定义身份验证入口点来处理不存在承载令牌的情况。结合一个自定义的BearerTokenAuthenticationFilter.authenticationFailureHandler来处理一个无效的令牌。我对其他方法持开放态度来满足这个目标。
我可以配置一个自定义身份验证入口点来处理不存在令牌的情况:

// in WebSecurityConfigurerAdapter::configure
http
    .exceptionHandling()
    .authenticationEntryPoint((request, response, exception) -> { /* ... */ });

但是我还没有找到访问BearerTokenAuthenticationFilter的方法。我能想到的最好的方法是按照我想要的方式配置一个新的服务器,但这对我没有吸引力,因为服务器最终会对每个成功认证的请求做额外的工作:

// in WebSecurityConfigurerAdapter::configure
var filter = new BearerTokenAuthenticationFilter(authenticationManagerBean());
filter.setAuthenticationFailureHandler(new JwtAuthenticationFailureHandler());
http.addFilterBefore(tokenAuth, BearerTokenAuthenticationFilter.class);
// my filter runs first

当然,在spring security创建的过滤器中有某种方法可以设置这个属性吗?理想情况下,它将由OAuth2ResourceServerConfigurer公开,但这只提供了accessDeniedHandler
我尝试过将过滤器本身或DefaultSecurityFilterChain作为bean访问,但它们在应用程序上下文中并不作为bean存在。我找到了this answer,它建议在spring-servlet.xml中配置一个bean,并通过后处理器运行它。BeanPostProcessor的想法似乎对我很有前途,但我无法让它工作。因为按照建议修改spring-servlet.xml之后,bean仍然不存在。我不能使用getBean来找到它,而且BeanPostProcessor也看不到它:

<http name="filterChain">
7lrncoxx

7lrncoxx1#

您要寻找的两种情况可以通过在不同级别应用AuthenticationEntryPoint的组合来处理,一个用于BearerTokenAuthenticationFilter,另一个用于过滤器链。例如:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer((oauth2) -> oauth2
            .jwt(Customizer.withDefaults())
            .authenticationEntryPoint((request, response, exception) -> {
                System.out.println("Authentication failed");
                BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                delegate.commence(request, response, exception);
            })
        )
        .exceptionHandling((exceptions) -> exceptions
            .authenticationEntryPoint((request, response, exception) -> {
                System.out.println("Authentication is required");
                BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                delegate.commence(request, response, exception);
            })
        );

    return http.build();
}

它以这种方式工作的原因是,当不存在承载令牌时,不会调用BearerTokenAuthenticationFilter。在这种情况下,将尝试整个过滤器链,但没有找到有效的身份验证。这将是Spring Security的正常“需要身份验证”场景。
然而,在令牌存在但无效的情况下,BearerTokenAuthenticationFilter需要验证令牌以确定其失败,并且在这种情况下使用其自己的AuthenticationFailureHandler。但是,失败处理程序在默认情况下仅委托给特定的AuthenticationEntryPoint,该AuthenticationEntryPoint是上面针对“认证失败”情况配置的AuthenticationEntryPoint
如果你想重写失败处理程序来做其他的事情(尽管在尝试了上面的方法之后你可能不需要这样做),你可以使用ObjectPostProcessor来完成。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .oauth2ResourceServer((oauth2) -> oauth2
            .jwt(Customizer.withDefaults())
            .withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
                @Override
                public <O extends BearerTokenAuthenticationFilter> O postProcess(O filter) {
                    filter.setAuthenticationFailureHandler((request, response, exception) -> {
                        System.out.println("Authentication failed (and is being handled in a custom way)");
                        BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                        delegate.commence(request, response, exception);
                    });
                    return filter;
                }
            })
        );

    return http.build();
}

相关问题