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

Spring Cloud Gateway启用CSRF后POST请求丢失formData致400错误

问题根源

Spring Cloud Gateway基于Reactor(响应式)实现,请求体(ServerHttpRequest的body)是Flux<DataBuffer>,只能被订阅消费一次。默认的CsrfWebFilter在处理POST类请求时,会尝试解析请求体以查找CSRF令牌(即便你的令牌放在请求头里),这会直接消费请求体,导致后续路由到上游服务时请求体已为空,上游用@RequestParam接收不到参数,最终返回400错误。

你尝试忽略FORM URL ENCODED类型但未生效,大概率是匹配规则配置错误,或是过滤器执行顺序导致忽略逻辑未触发。

解决方案

1. 正确配置CSRF过滤器,仅检查请求头令牌且不消费form-urlencoded请求体

修改网关的CSRF配置,指定令牌仅从请求头获取,同时排除application/x-www-form-urlencoded类型的请求,避免过滤器解析这类请求的body:

@Configuration
public class GatewayCsrfConfig {

    @Bean
    public CsrfWebFilter csrfWebFilter() {
        CsrfWebFilter filter = new CsrfWebFilter();
        // 指定CSRF令牌从请求头获取(Angular默认用X-XSRF-TOKEN)
        filter.setCsrfTokenRepository(new XorCsrfTokenRepository());
        filter.setRequestAttributeName(CsrfToken.class.getName());
        
        // 配置需要CSRF校验的请求规则:排除form-urlencoded类型,仅针对非安全HTTP方法
        filter.setRequiresCsrfMatcher(new AndServerWebExchangeMatcher(
            ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE),
            exchange -> {
                MediaType contentType = exchange.getRequest().getHeaders().getContentType();
                return contentType == null || !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType);
            }
        ));
        
        return filter;
    }
}

2. 确保自定义CsrfHeaderFilter不消费请求体

你的自定义过滤器仅需往响应头添加令牌,不要读取或修改请求体:

@Order(-100)
@Component
public class CsrfHeaderFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        CsrfToken csrfToken = exchange.getAttribute(CsrfToken.class.getName());
        if (csrfToken != null) {
            exchange.getResponse().getHeaders().add("X-XSRF-TOKEN", csrfToken.getToken());
        }
        // 直接传递请求,不触碰请求体
        return chain.filter(exchange);
    }
}

3. 调整过滤器执行顺序

通过@Order注解指定顺序:

  • 自定义CsrfHeaderFilter设置@Order(-100),确保在响应处理阶段执行,不干扰请求体传递
  • 默认CsrfWebFilter的默认顺序是Ordered.LOWEST_PRECEDENCE - 100,无需额外调整,会在路由过滤器之前执行

4. 验证上游服务参数接收配置

确认上游Servlet服务的POST端点正确配置:

@PostMapping(value = "/api/endpoint", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<String> handlePost(@RequestParam("param1") String param1, 
                                         @RequestParam("param2") String param2) {
    // 业务逻辑
    return ResponseEntity.ok("Success");
}
关键注意事项
  • 响应式请求体的一次性消费特性是核心问题,任何过滤器只要读取了请求体(哪怕只是解析),后续环节就无法再获取,必须确保CSRF过滤器不消费form-urlencoded请求的body
  • Angular默认会从响应头的X-XSRF-TOKEN获取令牌,后续POST请求会自动携带X-XSRF-TOKEN请求头,你的自定义过滤器逻辑是正确的,只需确保不干扰请求体传递

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 00:27:19