You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:23:17