Spring WebClient遇中间证书验证失败,寻求非JDK证书安装解决方案
问题分析与解决方案
核心原因
JDK默认信任库(cacerts)未包含USERTrust RSA Certification Authority根CA,而浏览器自带的信任库已内置该CA,因此浏览器访问正常但WebClient调用失败;DFN-Verein Certification Authority 2已在JDK默认信任库中,所以server-02调用无异常。
无需修改JDK全局信任库的解决方案
方案1:为WebClient自定义SSL上下文(推荐,仅影响当前WebClient)
直接在代码中加载目标CA证书(根CA+中间CA),生成专属信任管理器,配置到WebClient中,不影响JVM全局信任设置。
步骤:
- 导出
USERTrust RSA Certification Authority根CA和GEANT OV RSA CA 4中间CA的证书为PEM格式文件(可从浏览器证书详情导出,或CA官方渠道获取)。 - 构建自定义SslContext并配置WebClient:
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; import reactor.netty.http.client.HttpClient; import javax.net.ssl.TrustManagerFactory; import java.io.FileInputStream; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; // 加载CA证书 List<X509Certificate> caCertificates = new ArrayList<>(); CertificateFactory cf = CertificateFactory.getInstance("X.509"); // 加载根CA证书 try (FileInputStream fisRoot = new FileInputStream("/path/to/USERTrust_RSA_Certification_Authority.pem")) { caCertificates.add((X509Certificate) cf.generateCertificate(fisRoot)); } // 加载中间CA证书 try (FileInputStream fisIntermediate = new FileInputStream("/path/to/GEANT_OV_RSA_CA_4.pem")) { caCertificates.add((X509Certificate) cf.generateCertificate(fisIntermediate)); } // 构建信任库 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null); for (int i = 0; i < caCertificates.size(); i++) { X509Certificate cert = caCertificates.get(i); trustStore.setCertificateEntry("ca-cert-" + i, cert); } // 创建信任管理器工厂 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // 构建SslContext SslContext sslContext = SslContextBuilder.forClient() .trustManager(tmf) .build(); // 配置WebClient HttpClient httpClient = HttpClient.create().secure(sslSpec -> sslSpec.sslContext(sslContext)); WebClient reactiveWebClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build();
使用上述自定义的reactiveWebClient调用server-01的API即可正常验证证书。
方案2:通过JVM启动参数指定自定义信任库
创建包含目标CA的独立信任库,启动应用时通过JVM参数指定使用该信任库,无需修改JDK默认cacerts:
- 用
keytool将CA证书导入自定义信任库:
keytool -importcert -file /path/to/USERTrust_RSA_Certification_Authority.pem -alias usertrust-root -keystore ./custom-truststore.jks -storepass changeit
(若需添加中间CA,重复上述命令导入即可)
- 启动应用时添加JVM参数:
java -Djavax.net.ssl.trustStore=./custom-truststore.jks -Djavax.net.ssl.trustStorePassword=changeit -jar your-app.jar
每年更换证书时,仅需更新custom-truststore.jks文件即可。
方案3:排查服务器证书链完整性
虽然你提到openssl验证有效,仍可再次确认server-01是否返回完整证书链:
openssl s_client -connect server-01:443 -showcerts
若输出的证书链缺少GEANT OV RSA CA 4中间CA,需在服务器配置中补充完整证书链(将根CA、中间CA与服务器证书合并为链文件配置),WebClient即可自动获取完整链完成验证。
内容的提问来源于stack exchange,提问作者Thomas Lang
相关产品推荐
相关产品推荐

