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

升级Spring Boot3.2+JDK17后本地单元测试出现SunCertPathBuilderException

问题分析与解决方案

核心问题出在Spring Boot 3.2(基于Spring Framework 6.x)对RestTemplate的默认SSL上下文构建逻辑做了调整,结合JDK 17的SSL验证特性,导致本地环境无法识别公司内部URL的CA证书;而CI/CD环境因提前配置了信任库或打包时嵌入了证书,所以不受影响。另外Spring Boot 2.7+JDK17的组合没问题,是因为旧版本RestTemplate默认沿用了系统信任库的兼容逻辑。

排查步骤

  • 确认本地JDK的默认信任库($JAVA_HOME/lib/security/cacerts)是否包含公司内部服务的CA证书,CI/CD环境通常会提前导入这类证书或使用自定义信任库。
  • 检查单元测试中RestTemplate的实例来源:是Spring自动配置的实例,还是自定义配置的?Spring Boot 3.x的RestTemplateAutoConfiguration对默认客户端的选择(比如切换到HttpClient 5)可能导致SSL加载逻辑变化。
  • 验证本地测试的JDK版本:确保运行测试的JDK和CI环境一致(比如都是OpenJDK 17),避免Oracle JDK和OpenJDK的信任库差异。

解决方法

方法1:手动给RestTemplate配置自定义信任管理器

在测试配置类中创建包含公司CA证书的RestTemplate实例,确保单元测试使用该实例:

@Configuration
@TestConfiguration
public class TestRestTemplateConfig {

    @Bean
    public RestTemplate testRestTemplate() throws Exception {
        // 加载放在src/test/resources下的公司内部CA证书
        InputStream certStream = getClass().getResourceAsStream("/company-ca.crt");
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        X509Certificate caCert = (X509Certificate) certFactory.generateCertificate(certStream);
        certStream.close();

        // 初始化信任库并添加证书
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null);
        trustStore.setCertificateEntry("internal-ca", caCert);

        // 构建信任管理器
        TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmFactory.init(trustStore);

        // 创建SSL上下文
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmFactory.getTrustManagers(), new SecureRandom());

        // 配置HttpClient使用自定义SSL上下文
        HttpClient httpClient = HttpClientBuilder.create()
                .setSSLContext(sslContext)
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);

        return new RestTemplate(requestFactory);
    }
}

之后在单元测试中注入这个testRestTemplate即可。

方法2:导入证书到本地JDK信任库

使用keytool命令将公司CA证书导入本地JDK的默认信任库:

keytool -import -trustcacerts -alias internal-ca -file /path/to/company-ca.crt -keystore $JAVA_HOME/lib/security/cacerts

默认密码为changeit,导入后重启单元测试即可。注意要确保测试使用的JDK是你导入证书的那个版本。

方法3:通过系统属性指定自定义信任库

如果不想修改默认JDK信任库,可以在单元测试启动时添加VM参数,指定自定义信任库路径:

-Djavax.net.ssl.trustStore=/path/to/your/custom-truststore.jks
-Djavax.net.ssl.trustStorePassword=your-password

可以在IDE的测试运行配置中添加这些参数,或者在Maven/Gradle的测试配置中指定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:29:49