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

Spring Security多路径认证冲突:API Key过滤器误影响表单登录

问题原因

你遇到的核心问题是**ApiKeyAuthFilter被Spring注册为全局Servlet过滤器**,而非仅属于/api/**对应的SecurityFilterChain。即使请求不匹配/api/**路径,这个过滤器也会在所有SecurityFilterChain执行前被调用,直接返回401响应,导致表单登录流程根本没机会触发。

日志里显示的是SecurityFilterChain的匹配情况,但全局过滤器的执行优先级高于Spring Security的过滤器链,所以日志记录的环节已经是过滤器执行之后的流程了。

解决方案

要让ApiKeyAuthFilter仅作用于/api/**路径的过滤器链,需要做以下修改:

1. 移除过滤器类上的@Component注解

首先去掉ApiKeyAuthFilter类上的@Component,避免Spring自动将其注册为全局过滤器:

// 移除@Component注解
public class ApiKeyAuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        var request = (HttpServletRequest) servletRequest;
        var response = (HttpServletResponse) servletResponse;
        var apiKey = request.getHeader("ApiKey");
        if (apiKey == null) {
            unauthorized(response);
            return;
        }
        if (!apiKey.equals("sillytest")) {
            unauthorized(response);
            return;
        }
        filterChain.doFilter(request, response);
    }

    // 其他方法保持不变
}

2. 在API过滤器链中手动添加该过滤器

有两种方式实现:

方式一:通过@Bean单独定义过滤器实例

在配置类中添加ApiKeyAuthFilter的@Bean,让Spring管理其实例,然后通过构造注入到配置类中:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class MyLinksConfiguration {
    // 构造注入保持不变
    private final ApiKeyAuthFilter apiKeyAuthFilter;
    private final UnauthorizedHandler unauthorizedHandler;

    // 添加这个Bean定义
    @Bean
    public ApiKeyAuthFilter apiKeyAuthFilter() {
        return new ApiKeyAuthFilter();
    }

    // 其他Bean定义保持不变...

    @Bean
    @Order(1)
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
        return http.securityMatcher("/api/**")
            .addFilterBefore(apiKeyAuthFilter, ChannelProcessingFilter.class)
            .exceptionHandling(configurer -> configurer.authenticationEntryPoint(unauthorizedHandler))
            .cors(AbstractHttpConfigurer::disable)
            .csrf(AbstractHttpConfigurer::disable)
            .build();
    }

    // 表单登录过滤器链保持不变...
}

方式二:直接在过滤器链中创建实例

如果不需要Spring管理ApiKeyAuthFilter的生命周期,也可以直接在apiFilterChain中new出过滤器实例:

@Bean
@Order(1)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
    return http.securityMatcher("/api/**")
        .addFilterBefore(new ApiKeyAuthFilter(), ChannelProcessingFilter.class)
        .exceptionHandling(configurer -> configurer.authenticationEntryPoint(unauthorizedHandler))
        .cors(AbstractHttpConfigurer::disable)
        .csrf(AbstractHttpConfigurer::disable)
        .build();
}

额外说明

如果不想修改过滤器的@Component注解,也可以在过滤器的doFilter方法开头添加路径判断,仅处理/api/**的请求:

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    var request = (HttpServletRequest) servletRequest;
    // 仅处理/api开头的请求,其他请求直接放行
    if (!request.getRequestURI().startsWith("/api/")) {
        filterChain.doFilter(request, servletResponse);
        return;
    }
    // 原有API Key校验逻辑...
}

但这种方式不如前两种优雅,因为过滤器仍会被全局调用,只是跳过了非/api路径的处理。

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:00:53