如何在 BearerTokenAuthenticationFilter 上设置身份验证失败处理程序?

Flo*_*oky 3 java spring spring-security oauth-2.0 spring-boot

我能够使用 创建过滤器链http.oauth2ResourceServer().jwt()并且我还设置了spring.security.oauth2.resourceserver.jwt.issuer-uri. 它能够验证请求。但是,我还需要在身份验证失败的情况下进行自定义日志记录。我采用的方法是使用自定义身份验证入口点来处理不存在持有者令牌的情况,并结合自定义BearerTokenAuthenticationFilter.authenticationFailureHandler来处理无效令牌。我愿意接受其他方法来实现这一目标。

我可以配置自定义身份验证入口点来处理不存在令牌的情况:

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

但是我还没有找到访问 BearerTokenAuthenticationFilter 的方法。我能想到的最好的办法就是按照我想要的方式新建第二个配置,但这对我来说没有吸引力,因为服务器最终会对每个成功验证的请求执行额外的工作:

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

当然有某种方法可以在 Spring Security 创建的过滤器中设置此属性吗?理想情况下,它会被暴露出来OAuth2ResourceServerConfigurer,但这只是提供accessDeniedHandler

我尝试过以 bean 的形式访问过滤器本身或 DefaultSecurityFilterChain,但它们在应用程序上下文中不以 bean 形式存在。我发现这个答案建议配置一个beanspring-servlet.xml并通过后处理器运行它。BeanPostProcessor 的想法对我来说似乎很有希望,但我无法让它工作,因为按照建议修改后,spring-servlet.xmlbean 仍然不存在。我无法使用getBean它来找到它,并且它也没有被以下人看到BeanPostProcessor

<http name="filterChain">
Run Code Online (Sandbox Code Playgroud)

Ste*_*erg 9

您正在寻找的两种情况可以通过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();
}
Run Code Online (Sandbox Code Playgroud)

它以这种方式工作的原因是,BearerTokenAuthenticationFilter当不存在不记名令牌时,不会调用 。在这种情况下,会尝试整个过滤器链,但找不到有效的身份验证。这将是 Spring Security 的正常“需要身份验证”场景。

但是,在存在令牌但无效的情况下,BearerTokenAuthenticationFilter需要验证令牌以确定其失败,并AuthenticationFailureHandler在这种情况下使用自己的令牌。但默认情况下,失败处理程序只是委托给特定的处理程序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();
}
Run Code Online (Sandbox Code Playgroud)