如何使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
相关产品推荐
相关产品推荐

