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

Spring Security过滤器链与CSRF集成问题求助

Spring Security CSRF集成问题排查与正确方案

错误点分析

  1. 过滤器顺序配置错误
    CustomHeaderVerificationFilter的位置可能干扰CSRF校验流程:如果过滤器在CsrfFilter之后执行,CSRF校验已完成并返回403,自定义头校验根本没触发;如果在之前执行但修改了请求参数/头,会导致CSRF Token无法被Spring Security正确读取。

  2. 废弃CSRF配置方法误用
    Spring Security 6+中无参.csrf()方法已废弃,必须使用lambda式配置(csrf(csrf -> ...)),旧写法会导致CSRF校验逻辑未正确初始化,出现非预期拦截。

  3. POST请求未正确传递CSRF Token
    Spring Security默认校验POST/PUT/DELETE等修改类请求的CSRF Token,若请求中未携带符合要求的Token(请求头X-XSRF-TOKEN或参数_csrf),直接返回403。

  4. 自定义CSRF过滤器逻辑缺陷
    自定义过滤器仅对GET请求生效,说明逻辑中只处理了GET请求的Token生成,未对POST等请求做Token校验,或是请求方法判断逻辑错误。

正确CSRF集成方案

1. 适配新版本的CSRF配置

使用Spring Security 6+推荐的lambda式配置,指定Token存储方式和请求处理器:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        // 其他配置(如授权规则、认证方式)
        .csrf(csrf -> csrf
            // 用Cookie存储CSRF Token,允许前端JS读取(HttpOnly=false)
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            // 处理请求中的Token(兼容XOR加密的Token)
            .csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler())
        )
        // 自定义头校验过滤器放在CsrfFilter之前,先校验头再做CSRF校验
        .addFilterBefore(new CustomHeaderVerificationFilter(), CsrfFilter.class);
    
    return http.build();
}

2. POST请求的CSRF Token传递方式

无需提前通过GET请求获取Token(GET请求会自动在Cookie中写入XSRF-TOKEN),前端只需:

  • 从Cookie中读取XSRF-TOKEN的值
  • 在POST/PUT/DELETE请求的请求头中添加X-XSRF-TOKEN: [Token值]

3. 自定义CSRF过滤器的正确实现

如果必须自定义CSRF逻辑,需确保覆盖所有需要校验的请求方法,示例如下:

public class CustomCsrfFilter extends OncePerRequestFilter {
    private final CsrfTokenRepository tokenRepo = CookieCsrfTokenRepository.withHttpOnlyFalse();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 加载或生成Token
        CsrfToken csrfToken = tokenRepo.loadToken(request);
        if (csrfToken == null) {
            csrfToken = tokenRepo.generateToken(request);
            tokenRepo.saveToken(csrfToken, request, response);
        }
        // 将Token放入请求属性,供后续流程使用
        request.setAttribute(CsrfToken.class.getName(), csrfToken);
        request.setAttribute(csrfToken.getParameterName(), csrfToken);

        // 仅校验非安全方法(跳过GET/HEAD/OPTIONS/TRACE)
        String method = request.getMethod();
        if (!HttpMethod.GET.name().equals(method) && !HttpMethod.HEAD.name().equals(method)
                && !HttpMethod.OPTIONS.name().equals(method) && !HttpMethod.TRACE.name().equals(method)) {
            // 从请求头获取Token
            String providedToken = request.getHeader("X-XSRF-TOKEN");
            // 校验Token有效性
            if (providedToken == null || !providedToken.equals(csrfToken.getToken())) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid CSRF Token");
                return;
            }
        }

        chain.doFilter(request, response);
    }
}

配置时需禁用默认CsrfFilter,添加自定义过滤器:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        // 禁用默认CSRF过滤器,使用自定义实现
        .csrf(csrf -> csrf.disable())
        .addFilterBefore(new CustomCsrfFilter(), UsernamePasswordAuthenticationFilter.class)
        .addFilterBefore(new CustomHeaderVerificationFilter(), CustomCsrfFilter.class);
    
    return http.build();
}

4. 过滤器顺序注意事项

  • 自定义头校验过滤器(CustomHeaderVerificationFilter)应放在CSRF过滤器之前:先校验自定义头合法性,再执行CSRF校验
  • CSRF过滤器(默认或自定义)应放在认证过滤器(如UsernamePasswordAuthenticationFilter)之前

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:05:36