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

SpringBootTest集成测试中禁用安全校验及安全测试实现方案(基于RestTestClient.bindToServer())

SpringBootTest集成测试中禁用安全校验及安全测试实现方案(基于RestTestClient.bindToServer())

我来帮你分析问题并给出针对性的解决方案,先解释下你之前尝试的方法为什么没生效:

  • @WithMockUser/@WithAnonymousUser无效:这类注解是模拟测试线程的认证上下文,但RestTestClient是发送真实HTTP请求到服务器,服务器的处理线程和测试线程完全独立,上下文无法共享,所以自然不生效。
  • 排除SecurityAutoConfiguration没生效:Spring Boot的安全自动配置包含多个关联类(比如SecurityFilterAutoConfiguration),如果你的项目还有自定义安全配置类,排除单个AutoConfig类无法完全覆盖原有规则。
  • @AutoConfigureMockMvc(addFilters = false)无效:这个注解是针对MockMvc模拟测试的,和你用的真实服务器绑定的RestTestClient完全不相关,所以不起作用。

一、如何禁用安全校验(让测试通过)

最可靠的方式是在测试中添加测试专用的安全配置,直接覆盖原有安全规则,不会影响主应用的配置。

方案:添加测试专用SecurityFilterChain

修改你的测试类,新增一个静态的测试配置类,定义允许所有请求的安全过滤链:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoControllerIntegrationTest {

    @LocalServerPort
    private int port;
    private RestTestClient restClient;

    @BeforeEach
    void beforeEachTest() {
        restClient = RestTestClient.bindToServer()
                                   .baseUrl("http://localhost:" + port)
                                   .build();
    }

    // 测试专用安全配置:允许所有请求,禁用不必要的CSRF
    @TestConfiguration
    static class TestSecurityConfig {
        @Bean
        public SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception {
            http
                .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
                .csrf(csrf -> csrf.disable()); // 测试场景下通常不需要CSRF保护
            return http.build();
        }
    }

    @Test
    void should_return_uppercase_request_uri_unsecured_home() {
        String uri = "/unsecured/home";
        String expected = "/UNSECURED/HOME";
        restClient.get()
                  .uri(uri)
                  .exchange()
                  .expectStatus()
                  .isOk()
                  .expectBody(String.class)
                  .isEqualTo(expected);
    }
}

这样测试启动时会优先加载这个测试配置,替换原有安全规则,所有请求都会被允许,你的测试就能正常通过了。


二、正确实现安全测试(带认证请求)

因为RestTestClient绑定真实服务器,必须通过HTTP请求传递真实认证信息,以下是几种常见认证场景的实现:

1. Basic 用户名密码认证

可以全局设置默认认证头,或单个请求单独设置:

// 全局设置:所有请求自动带上Basic认证
@BeforeEach
void beforeEachTest() {
    restClient = RestTestClient.bindToServer()
                               .baseUrl("http://localhost:" + port)
                               .defaultHeaders(headers -> {
                                   // 替换为你的测试用用户名密码
                                   headers.setBasicAuth("test-user", "test-123");
                               })
                               .build();
}

// 单个请求单独设置
@Test
void testSecuredEndpoint() {
    restClient.get()
              .uri("/secured/info")
              .headers(headers -> headers.setBasicAuth("test-user", "test-123"))
              .exchange()
              .expectStatus().isOk();
}

2. JWT Token 认证

先获取测试用JWT,再在请求头中携带:

@BeforeEach
void beforeEachTest() {
    // 1. 先调用登录接口获取JWT Token
    String jwtToken = restClient.post()
                                .uri("/auth/login")
                                .body("{\"username\":\"test-user\",\"password\":\"test-123\"}")
                                .exchange()
                                .expectBody(String.class)
                                .returnResult()
                                .getResponseBody();

    // 2. 初始化RestClient时默认携带Token
    restClient = RestTestClient.bindToServer()
                               .baseUrl("http://localhost:" + port)
                               .defaultHeaders(headers -> {
                                   headers.setBearerAuth(jwtToken);
                               })
                               .build();
}

3. 表单登录(会话认证)

先完成登录获取会话Cookie,后续请求携带该Cookie:

@Test
void testSecuredPageWithFormLogin() {
    // 1. 发送登录请求,获取会话Cookie
    ClientResponse loginResponse = restClient.post()
                                            .uri("/login")
                                            .body("{\"username\":\"test-user\",\"password\":\"test-123\"}")
                                            .exchange()
                                            .expectStatus().is3xxRedirection()
                                            .returnResult(Void.class)
                                            .getResponse();

    // 提取会话Cookie
    String sessionCookie = loginResponse.headers().firstHeader(HttpHeaders.SET_COOKIE);

    // 2. 携带Cookie访问受保护接口
    restClient.get()
              .uri("/secured/dashboard")
              .headers(headers -> headers.add(HttpHeaders.COOKIE, sessionCookie))
              .exchange()
              .expectStatus().isOk();
}

备注:内容来源于stack exchange,提问作者Saravana Kumar M

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 18:08:05