【问题标题】:Spring boot filter called twice or not called at allSpring Boot 过滤器调用了两次或根本不调用
【发布时间】:2021-01-22 06:18:58
【问题描述】:

我实现了一个 customFilter,它将请求的 cookie 中的某些内容添加到其标头中:

@Component
@Slf4j
public class MyCustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
        .... some logic...
        log.info("Sending request to next chain for validation..");
        chain.doFilter(request, response);
        log.info("Authentication completed sucessfully");
    }

    @Bean
    // This method is needed to replace the default cookieFilter.json processor of tomcat that ignores the jwt cookieFilter.json
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
        return tomcatServletWebServerFactory -> tomcatServletWebServerFactory.addContextCustomizers((TomcatContextCustomizer) context -> {
            context.setCookieProcessor(new LegacyCookieProcessor());
        });
    }

}

我的 WebSecurityConfigurerAdapter 类:

@Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //configuring strategy
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt().and();
        http.csrf().disable();
        http.addFilterBefore(new MyCustomFilter (), UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint());
    }

}

当我运行代码并通过邮递员/curl 发送请求时,我看到过滤器在

Sending request to next chain for validation..
Sending request to next chain for validation..
Authentication completed sucessfully
Authentication completed sucessfully

我发现了一些关于问题的帖子,并尝试了以下解决方案:

  1. 这是因为 spring 自动注册了 beans,我在 configure 方法中手动添加了过滤器。因此,我删除了 configure() 方法中手动添加的过滤器。结果是过滤器根本没有被调用。

  2. 尝试扩展OncePerRequestFilter类,而不是实现过滤器接口。这样做了,但过滤器仍然触发了两次。

  3. 还尝试删除 @Component 注释并手动添加过滤器。此外,我必须将 CookieProcessor bean 移动到 Configuration 类。之后出现的问题是应用无法启动,原因如下:

    原因:org.springframework.beans.BeanInstantiationException:无法实例化[org.springframework.web.servlet.HandlerMapping]:工厂方法'resourceHandlerMapping'抛出异常;嵌套异常是 java.lang.IllegalStateException: No ServletContext set

我使用的是 spring-security 版本 5.3.3。

【问题讨论】:

  • 删除@Component。它目前是普通过滤器链和安全过滤器链的一部分,因此它被执行了两次(它也被注册了两次)。 @Bean 方法应该在 @Configuration 类上,而不是在 @Component 上。
  • 我将 bean 移动到配置中,但出现以下错误: 原因:org.springframework.beans.BeanInstantiationException:无法实例化 [org.springframework.web.servlet.HandlerMapping]:工厂方法“resourceHandlerMapping”抛出异常;嵌套异常是 java.lang.IllegalStateException: No ServletContext set
  • 返回 TomcatContextCustomizer 并且不要添加它。你把事情弄得太复杂了。
  • 我使用了以下代码,但仍然出现相同的错误: public TomcatContextCustomizer cookieProcessorCustomizer() { return context -> context.setCookieProcessor(new LegacyCookieProcessor()); }

标签: java spring spring-boot spring-security


【解决方案1】:

根据经验,不要将@Bean 方法添加到@Component 类,因为它们的处理方式与@Configuration 类中的方法不同。 (见this)。

@Bean 中的代码太复杂了。创建并返回 TomcatContextCustomizer 以进行修改。您的代码将导致循环引用,这将导致初始化错误。

将以下@Bean 方法添加到您的@SpringBootApplication 注释类

@Bean
public TomactContextCustomizer cookieProcessorCustomizer() {
  return (context) -> context.setCookieProcessor(new LegacyCookieProcessor());
}

现在在您的Filter 中删除@Component 添加一个随附的FilterRegistrationBean 以防止它被添加到常规过滤器链中。 (Spring Boot 自动将所有检测到的Filter 实例注册到常规过滤器链中)。

@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistrationBean(MyFilter myFilter) {
  FilterRegistrationBean<MyFilter> frb = new FilterRegistrationBean<>(myFilter);
  frb.setEnabled(false);
  return frb;
}

如果您删除 @Component,则不需要上述 sn-p,那么您应该在安全配置中重复使用扫描的 MyFilter 实例。

@Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyFilter myFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //configuring strategy
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt().and();
        http.csrf().disable();
        http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint());
    }

}

【讨论】:

  • 首先感谢您的清晰解释。我删除了@Component 注释,因此我没有传递注入的 bean,而是创建了一个新的过滤器实例,并将其传递给 addFilterBefore(..)。我试图运行它,但我得到了同样的错误:(
  • 您还需要TomcatContextCustomizer,否则LegacyCookieProcessor 将不会被注册。
  • 我没有写,但我将 TomactContextCustomizer Bean 添加到我的@Configuraiton 类中
  • 当我在没有 tomcatContextCustomizer 方法的情况下运行代码时,spring 应用程序会启动,但我的 cookie 会被忽略。使用定制器的@Bean方法,spring app因为我前面提到的上下文错误而无法启动
  • 解决方案是将 TomcatContextCustomizer 移动到不同的 @Configuration 类,而不是将其保留在扩展 WebSecurityConfigurerAdapter 的类中。谢谢大家的帮助!!
猜你喜欢
  • 2015-08-19
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
  • 2019-01-06
  • 2017-01-11
相关资源
最近更新 更多