Spring Security过滤器链与CSRF集成问题求助
Spring Security CSRF集成问题排查与正确方案
错误点分析
过滤器顺序配置错误
CustomHeaderVerificationFilter的位置可能干扰CSRF校验流程:如果过滤器在CsrfFilter之后执行,CSRF校验已完成并返回403,自定义头校验根本没触发;如果在之前执行但修改了请求参数/头,会导致CSRF Token无法被Spring Security正确读取。废弃CSRF配置方法误用
Spring Security 6+中无参.csrf()方法已废弃,必须使用lambda式配置(csrf(csrf -> ...)),旧写法会导致CSRF校验逻辑未正确初始化,出现非预期拦截。POST请求未正确传递CSRF Token
Spring Security默认校验POST/PUT/DELETE等修改类请求的CSRF Token,若请求中未携带符合要求的Token(请求头X-XSRF-TOKEN或参数_csrf),直接返回403。自定义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
相关产品推荐
相关产品推荐

