Spring自定义过滤器总是被调用

9

我有一个自定义过滤器,在BasicAuthenticationFilter之前被调用,该Bean在SecurityConfig文件中进行了自动装配。

.addFilterBefore(preAuthTenantContextInitializerFilter, BasicAuthenticationFilter.class)

这是过滤器的外观。

@Component
public class PreAuthTenantContextInitializerFilter extends OncePerRequestFilter {
    @Autowired
    private TenantService tenantService;
.....
.....

我希望这个过滤器不像Spring Security过滤器链中的其他过滤器一样触发,对于在WebSecurityConfigurerAdapter#configure(WebSecurity web) web.ignoring()中包含的路径。以下是它的外观。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", 
                                    "/configuration/security", "/swagger-ui.html", 
                                    "/webjars/**","/swagger-resources/configuration/ui",
                                    "/swagge‌​r-ui.html", "/docs/**");
    }
}

我已经尝试过的。

从过滤器类中删除@Component注释,因为过滤器不再作为bean选取,也永远不会进入过滤器链并触发。

我正在寻找的

我希望这个过滤器在调用Spring安全链的其余部分时被调用,并且像其他Spring安全过滤器一样,在web.ignoring()中忽略路径。谢谢。


4
因为它是一个bean,Spring Boot会检测到它并将其添加到常规过滤器链中。由于OncePerRequestFilter的扩展,它只会执行一次,基本上在安全过滤器链中配置的入口什么也不做。要修复它,请勿将其设置为bean并自己连接依赖项。这样,Spring Boot就不会检测到它,它将只成为Spring Security过滤器链的一部分,而不是常规过滤器链的一部分。或者为过滤器添加一个FilterRegistrationBean,并将enabled设置为false以防止它被自动注册。 - M. Deinum
2个回答

19
任何作为 Spring bean 的 Servlet、Filter 或 Servlet *Listener 实例都将被注册到内嵌容器中。如果您想在配置期间从 application.properties 引用值,这将特别方便。
这段代码摘自 Spring Boot 参考指南。基本上,应用程序上下文中检测到的任何 Filter 都将被注册到默认的 filter chain,并映射到 DispatcherServlet 或 / 。
在您的情况下,由于该过滤器被标记为 @Component,它将成为一个 bean,Spring Boot 检测到它并将其注册到内嵌容器中。然而,您不希望这样,因为您只希望它成为 Spring Security filter chain 的一部分。
要实现此目的,您有两个选择:
1. 删除 @Component 和 @Autowired,并构造自己的实例,不要将其设置为 bean。 2. 添加一个额外的 [FilterRegistrationBean] 并将 enabled 属性设置为 false,这将防止 Spring Boot 将其注册到内嵌容器中。
以下是选项 2 的解决方案:
@Bean
public FilterRegistrationBean preAuthTenantContextInitializerFilterRegistration(PreAuthTenantContextInitializerFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
}

添加这个 bean 后,Spring Boot 将不会把过滤器注册到嵌入式容器中,因此它只会作为 Spring Security 过滤器链的一部分被调用。


1
谢谢。这是我错过的关键部分。通过将过滤器注释为@Component,它会自动注册到底层容器过滤器链中,并且还会添加到Spring安全过滤器链中(如果您使用addFilterBefore或addFilterAfter手动添加)。因此,现在该过滤器被调用两次。 - SeaBiscuit

2
  • I presume you are trying to ignore security for loading the Swagger UI page. If that's the case you need to add another path, /swagger-resources/configuration/security, to your list of ignored paths. Once you add this path, your PreAuthTenantContextInitializerFilter will not be invoked when you call any of the ignored paths.
  • Also, manage the creation of the PreAuthTenantContextInitializerFilter manually instead of having Spring managed. It will involve removing @Component from your filter. You can find a working example here.

    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
       private TenantService service;
    
       @Autowired
       public SecurityConfiguration(TenantService service) {
           this.service = service;
       }
    
       @Override
       protected void configure(HttpSecurity http) throws Exception {
           http.addFilterBefore(
               new PreAuthTenantContextInitializerFilter(service),
               BasicAuthenticationFilter.class);
           ...
       }
    
       @Override
       public void configure(WebSecurity web) throws Exception {
           web.ignoring().antMatchers("/v2/api-docs", 
                "/configuration/ui",
                "/swagger-resources",
                "/configuration/security", "/swagger-ui.html",
                "/webjars/**", "/swagger-resources/configuration/ui",
                "/swagge‌r-ui.html", "/docs/**",
                "/swagger-resources/configuration/security");
       }
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接