英文:
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
我找到了一些关于此问题的帖子,尝试了以下解决方案:
- 这是因为Spring会自动注册Bean,并且我在
configure
方法中手动添加了过滤器。因此,我从configure()
方法中移除了手动添加过滤器的部分。结果是过滤器根本没有被调用。 - 尝试继承
OncePerRequestFilter
类,而不是实现过滤器接口。尽管如此,过滤器仍然触发了两次。 - 还尝试过移除
@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("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());
});
}
}
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 :
-
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.
-
Instead of implementing the filter interface, try to extend the
OncePerRequestFilter
class. Done that, but the filter still triggered twice. -
Tried also to remove the
@Component
annotation and add the filter manually. In addition I had to move theCookieProcessor
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) -> 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
,则不需要上述代码片段;如果您没有删除,那么您应该在安全配置中重复使用已扫描到的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) -> 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<MyFilter> myFilterRegistrationBean(MyFilter myFilter) {
FilterRegistrationBean<MyFilter> frb = new FilterRegistrationBean<>(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());
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论