Spring中自定义StaticKeyAuthFilter执行两次问题求助
过滤器执行两次的问题排查与解决
问题描述
我编写了一个静态密钥认证过滤器,用@Component注解读取application.yml中的auth.key配置,然后在Spring Security配置类中注册该过滤器。但现在每次访问控制器时,过滤器都会执行两次,控制台重复输出日志。
过滤器代码
@Component public class StaticKeyAuthFilter implements Filter { @Value("${auth.key}") private String authKey; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String authorization = request.getHeader("Authorization"); System.out.println("StaticKeyAuthFilter:" + request.getRequestURI() ); if (authorization == null || !authorization.equals(authKey)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } filterChain.doFilter(request, response); } }
配置类代码
@Configuration public class ProjectConfig { @Autowired private StaticKeyAuthFilter staticKeyAuthFilter; @Bean SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeHttpRequests(c -> { c.anyRequest().permitAll(); }); httpSecurity.addFilterAt(staticKeyAuthFilter, BasicAuthenticationFilter.class); return httpSecurity.build(); } }
控制台输出
StaticKeyAuthFilter:/category/bb StaticKeyAuthFilter:/category/bb
原因分析
问题出在过滤器被双重注册:
- 给过滤器添加
@Component注解后,Spring会自动将其识别为Servlet过滤器,并注册到Servlet容器的全局过滤器链中。 - 同时你又在
SecurityFilterChain里通过addFilterAt方法,手动把同一个过滤器实例添加到了Spring Security的专属过滤器链中。 - 同一个过滤器被加入两个不同的执行链,自然会在每次请求时被执行两次。
解决方案
两种修复方式二选一即可:
方式一:移除过滤器上的@Component注解
保留Spring Security中的注册逻辑,同时去掉过滤器类上的@Component,避免Spring自动将其注册到Servlet容器。
修改后的过滤器代码:
// 移除@Component注解 public class StaticKeyAuthFilter implements Filter { private final String authKey; // 改用构造方法注入配置,避免@Value和setter public StaticKeyAuthFilter(String authKey) { this.authKey = authKey; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 原有逻辑不变 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String authorization = request.getHeader("Authorization"); System.out.println("StaticKeyAuthFilter:" + request.getRequestURI() ); if (authorization == null || !authorization.equals(authKey)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } filterChain.doFilter(request, response); } }
配置类中手动定义过滤器Bean并注入:
@Configuration public class ProjectConfig { @Bean StaticKeyAuthFilter staticKeyAuthFilter(@Value("${auth.key}") String authKey) { return new StaticKeyAuthFilter(authKey); } @Bean SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeHttpRequests(c -> { c.anyRequest().permitAll(); }); httpSecurity.addFilterAt(staticKeyAuthFilter(), BasicAuthenticationFilter.class); return httpSecurity.build(); } }
方式二:保留@Component,禁用Servlet容器自动注册
如果想保留@Component,可以通过FilterRegistrationBean禁用Servlet容器对该过滤器的自动注册,只让它在Spring Security链中执行:
@Configuration public class ProjectConfig { @Autowired private StaticKeyAuthFilter staticKeyAuthFilter; // 禁用Servlet容器自动注册该过滤器 @Bean FilterRegistrationBean<StaticKeyAuthFilter> disableFilterAutoRegistration(StaticKeyAuthFilter filter) { FilterRegistrationBean<StaticKeyAuthFilter> registrationBean = new FilterRegistrationBean<>(filter); registrationBean.setEnabled(false); return registrationBean; } @Bean SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeHttpRequests(c -> { c.anyRequest().permitAll(); }); httpSecurity.addFilterAt(staticKeyAuthFilter, BasicAuthenticationFilter.class); return httpSecurity.build(); } }
总结
两种方式核心都是避免过滤器被重复注册,推荐方式一,逻辑更清晰:过滤器仅属于Spring Security的认证链,不需要被Servlet容器全局管理。
内容的提问来源于stack exchange,提问作者Cody
相关产品推荐
相关产品推荐

