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

如何使Spring WebFlux集成测试适配CSRF验证?

Spring WebFlux集成测试开启CSRF后正常运行的解决方案

你遇到的问题根源在于:mutateWith(csrf())是为Servlet栈的MockMvc设计的测试工具,并不适配WebFlux的WebTestClient,强行使用就会触发空指针异常。针对响应式场景,有两种可靠的解决方式:

方式一:手动获取并携带CSRF Token

因为你用了CookieServerCsrfTokenRepository,CSRF Token会以Cookie(XSRF-TOKEN)的形式返回,只需先发起一个GET请求获取该Cookie,再在POST请求中通过X-XSRF-TOKEN头携带Token即可:

@Test
@WithUserDetails("john.doe")
void testDeleteUserAsOrganizationAdmin() {
    // 1. 发起任意GET请求,触发CSRF Cookie生成
    String csrfToken = webTestClient.get()
            .uri("/health") // 用项目中已有的公开GET端点即可,比如健康检查接口
            .exchange()
            .expectStatus().isOk()
            .returnResult(Void.class)
            .getResponseCookies()
            .getFirst("XSRF-TOKEN")
            .getValue();

    // 2. 发送POST请求时携带CSRF头
    webTestClient.post()
            .uri("/users/123") // 你的目标接口
            .header("X-XSRF-TOKEN", csrfToken)
            // 按需添加请求体或其他参数
            .exchange()
            .expectStatus().isOk(); // 按实际业务预期状态码调整
}

方式二:配置WebTestClient自动处理CSRF

如果不想每个测试都手动写获取Token的逻辑,可以自定义WebTestClient的过滤器,让它自动处理CSRF Token的获取和重发:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class UserControllerTest {
    
    private WebTestClient webTestClient;

    @Autowired
    void setupWebTestClient(WebTestClient.Builder builder) {
        this.webTestClient = builder
                .filter((request, chain) -> {
                    return chain.exchange(request)
                            .flatMap(response -> {
                                // 如果请求返回403,说明缺少CSRF Token
                                if (HttpStatus.FORBIDDEN.equals(response.statusCode())) {
                                    // 获取CSRF Token
                                    return webTestClient.get()
                                            .uri("/health")
                                            .exchange()
                                            .flatMap(csrfResp -> {
                                                String token = csrfResp.getResponseCookies()
                                                        .getFirst("XSRF-TOKEN")
                                                        .getValue();
                                                // 重新构建请求并携带Token
                                                ClientRequest newRequest = ClientRequest.from(request)
                                                        .header("X-XSRF-TOKEN", token)
                                                        .build();
                                                return chain.exchange(newRequest);
                                            });
                                }
                                return Mono.just(response);
                            });
                })
                .build();
    }

    @Test
    @WithUserDetails("john.doe")
    void testDeleteUserAsOrganizationAdmin() {
        // 直接发送请求,过滤器会自动处理CSRF
        webTestClient.post()
                .uri("/users/123")
                // 添加请求参数
                .exchange()
                .expectStatus().isOk();
    }
}

额外注意点

  • 确保你的GET端点确实会触发CSRF Cookie生成:如果接口被配置为不需要CSRF保护,可能不会生成Cookie,此时可以专门添加一个无业务逻辑的GET端点用于测试获取Token。
  • 如果你有部分端点不需要CSRF保护,可以在Security配置中通过.csrf(csrf -> csrf.ignoringExchangeMatchers("/some-endpoint"))排除,减少测试复杂度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 04:35:14