SpringBootTest随机端口下配置WebTestClient使用HTTPS的方案问询
问题:Spring Boot测试中配置WebTestClient使用HTTPS(随机端口场景)
我有一个REST服务,必须基于HTTPS实现TLSv1.3(本应用禁止使用HTTP)。当前测试失败,因为WebTestClient仍通过HTTP协议发起调用。我知道可以手动构建WebTestClient,但测试采用RANDOM_PORT(CI任务运行在多团队共享的Jenkins节点,无法安全移除该配置)。
测试代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.main.allow-bean-definition-overriding=true") @ContextConfiguration(classes = TestWebClientConfiguration.class) // contains beans to connect to mocked downstream services @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DirtiesContext @AutoConfigureWebTestClient @ActiveProfiles(value = {"test"}) public class HealthCheckTests { @Autowired private WebTestClient webTestClient; ... }
从应用日志可见,webTestClient的调用为:
GET http://localhost:19654/health-check
原本该调用正常,但现在需改为HTTPS。请问如何在使用@AutoConfigureWebTestClient时配置WebTestClient使用HTTPS?或者如何让WebTestClient指向Spring设置的随机端口?
已尝试的方案
更新1
尝试手动构建WebTestClient,但无法获取随机端口且证书不匹配:
private WebTestClient webTestClient; @PostConstruct void init() throws SSLException { // var sslContext = SSLContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build; // var httpClient = HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext)); webTestClient = WebTestClient.bindToServer().baseUrl("https://localhost:<fix_port>").build() }
更新2
成功配置SSLContext,但仍需硬编码端口:
@PostConstruct void init() throws SSLException { var sslContext = SSLContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build; var httpClient = HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext)); webTestClient = WebTestClient.bindToServer(new ReactorClientHttpConnector(httpClient)).baseUrl("https://localhost:<fixed-port>").build(); }
解决方法
1. 获取随机HTTPS端口
通过@LocalServerPort注解注入Spring Boot启动时分配的随机HTTPS端口,替代硬编码端口。
2. 配置WebTestClient使用HTTPS并信任测试证书
以下两种方案可同时满足随机端口和HTTPS调用需求:
方案一:自定义WebTestClient Bean
在测试配置类(如TestWebClientConfiguration)中定义WebTestClient Bean,注入端口并配置SSL:
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.netty.http.client.HttpClient; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; @Configuration public class TestWebClientConfiguration { @Value("${local.server.port}") private int serverPort; @Bean public WebTestClient webTestClient() throws SSLException { // 构建信任所有证书的SSL上下文(*仅测试环境使用*) SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) {} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) {} @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, null); HttpClient httpClient = HttpClient.create() .secure(sslSpec -> sslSpec.sslContext(sslContext)); return WebTestClient.bindToServer(new ReactorClientHttpConnector(httpClient)) .baseUrl("https://localhost:" + serverPort) .build(); } }
方案二:在测试类中直接初始化WebTestClient
如果不想修改配置类,可在测试类中注入端口并覆盖WebTestClient:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.main.allow-bean-definition-overriding=true") @ContextConfiguration(classes = TestWebClientConfiguration.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DirtiesContext @ActiveProfiles(value = {"test"}) public class HealthCheckTests { @LocalServerPort private int serverPort; private WebTestClient webTestClient; @PostConstruct void init() throws SSLException { SSLContext sslContext = SSLContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .build(); HttpClient httpClient = HttpClient.create() .secure(sslSpec -> sslSpec.sslContext(sslContext)); webTestClient = WebTestClient.bindToServer(new ReactorClientHttpConnector(httpClient)) .baseUrl("https://localhost:" + serverPort) .build(); } // 测试方法... }
关键说明
@LocalServerPort会自动注入Spring Boot启动时分配的随机端口(只要服务配置了HTTPS,此端口即为HTTPS端口)。- 使用
InsecureTrustManagerFactory.INSTANCE是快速实现信任所有证书的方式,生产环境绝对不能使用;若测试环境有自定义信任证书,需替换为对应的TrustManager。
内容的提问来源于stack exchange,提问作者user4932655
相关产品推荐
相关产品推荐

