春季启动过滤器被调用两次或根本未被调用

huangapple go评论72阅读模式
英文:

Spring boot filter called twice or not called at all

问题

我实现了一个自定义过滤器MyCustomFilter,它会将请求的cookie中的某些内容添加到请求头中:

@Component
@Slf4j
public class MyCustomFilter implements Filter {

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

    @Bean
    // 需要使用这个方法替换Tomcat的默认cookieProcessor.json处理器,以便处理jwt cookie
    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 {

        // 配置策略
        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());
    }

}

当我运行代码并通过Postman或Curl发送请求时,我注意到过滤器被触发两次:

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

我找到了一些关于此问题的帖子,尝试了以下解决方案:

  1. 这是因为Spring会自动注册Bean,并且我在configure方法中手动添加了过滤器。因此,我从configure()方法中移除了手动添加过滤器的部分。结果是过滤器根本没有被调用。
  2. 尝试继承OncePerRequestFilter类,而不是实现过滤器接口。尽管如此,过滤器仍然触发了两次。
  3. 还尝试过移除@Component注解,并手动添加过滤器。此外,我还将CookieProcessor bean 移动到了配置类中。然后出现了一个启动失败的问题,错误消息如下:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set

我正在使用Spring Security版本5.3.3。

英文:

I implemented a customFilter that adds something from the request`s cookies to its headers :

@Component
@Slf4j
public class MyCustomFilter implements Filter {

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

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

}

My WebSecurityConfigurerAdapter class :

@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());
    }

}

When I run the code and send a request via postman/curl I see that the filter triggered twice in the

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

I found a few posts about issue and I tried the following solutions :

  1. It happens because spring registers the beans automatically and I add the filter manually in the configure method. Therefore, I removed the manually addition of the filter in the configure() method. The result was that the filter wasnt called at all.

  2. Instead of implementing the filter interface, try to extend the OncePerRequestFilter class. Done that, but the filter still triggered twice.

  3. Tried also to remove the @Component annotation and add the filter manually. In addition I had to move the CookieProcessor bean to the Configuration class. The problem that raised afterwards is that the app fails to start because of the following error :

    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set

I am using spring-security version 5.3.3.

答案1

得分: 3

以下是翻译好的部分:

按照经验法则,不要将@Bean方法添加到@Component类中,因为这些方法与@Configuration类中的方法处理方式不同(请参见此处)。

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

在您的@SpringBootApplication注解的类中添加以下@Bean方法:

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

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

@Bean
public FilterRegistrationBean&lt;MyFilter&gt; myFilterRegistrationBean(MyFilter myFilter) {
  FilterRegistrationBean&lt;MyFilter&gt; frb = new FilterRegistrationBean&lt;&gt;(myFilter);
  frb.setEnabled(false);
  return frb;
}

如果您删除了@Component,则不需要上述代码片段;如果您没有删除,那么您应该在安全配置中重复使用已扫描到的MyFilter实例。

@Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyFilter myFilter;

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

        // 配置策略
        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());
    }

}
英文:

As a rule of thumb, don't add @Bean methods to @Component classes as those are handled differently than those in @Configuration classes. (See this).

The your code in the @Bean is too complex. Create and return a TomcatContextCustomizer to do the modification. Your code will lead to circulair references which will lead to initializing errors.

Add the following @Bean method to your @SpringBootApplication annotated class

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

Now in your Filter either remove the @Component or add an accompying FilterRegistrationBean to prevent it from being added to the regular chain of filters. (Spring Boot automatically registers all detected Filter instances to the regular filter chain).

@Bean
public FilterRegistrationBean&lt;MyFilter&gt; myFilterRegistrationBean(MyFilter myFilter) {
  FilterRegistrationBean&lt;MyFilter&gt; frb = new FilterRegistrationBean&lt;&gt;(myFilter);
  frb.setEnabled(false);
  return frb;
}

If you remove @Component the above snippet isn't needed if you don't then you should reuse the scanned MyFilter instance in your security configuration.

@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());
    }

}

huangapple
  • 本文由 发表于 2020年10月7日 17:53:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/64241555.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定