Spring Boot 使用 RestTemplate 调用 HTTPS 接口时报错:PKIX path building failed 解决方案

发布于:2025-08-30 ⋅ 阅读:(9) ⋅ 点赞:(0)

在使用 Spring Boot + RestTemplate 调用 HTTPS 接口时,很多同学会遇到类似下面的报错:

javax.net.ssl.SSLHandshakeException: 
PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

这类错误通常意味着:JVM 无法信任目标服务返回的证书

一、为什么会出现 PKIX path building failed?

PKIX path building failed 的意思是 证书链校验失败,JVM 在校验证书时没有找到可信的路径。常见原因有以下几种:

1. JDK 版本过旧

  • 较早的 JDK(如 JDK8u101 之前)默认内置的 CA 根证书库较老。

  • 如果目标网站使用了新 CA(例如 Let's Encrypt 2021 年更新过根证书),老 JDK 可能无法识别。

2. 服务器证书链不完整

  • 有些服务器只配置了 站点证书,但忘记安装 中间证书

  • 浏览器会自动帮你补全中间证书,所以能正常访问;

  • 但 JVM 不会自动补全,导致校验失败。

3. 证书域名不匹配

  • 如果访问 https://example.com,但证书只签发给了 api.example.com,也会校验失败。

4. 使用了自签名证书

  • 内网测试时常见,需要手动导入证书到 JDK 信任库。

二、如何排查?

  1. 检查证书链是否完整

    openssl s_client -connect yourdomain.com:443 -showcerts
    

    如果只输出一段证书而没有中间 CA,说明配置不完整。

  2. 检查域名是否匹配

    openssl s_client -connect yourdomain.com:443 | openssl x509 -noout -text | grep DNS:
    

    看 SAN 字段里是否包含你访问的域名。

  3. 检查 JDK 版本

    java -version
    

    建议 JDK8 至少升级到 8u202+ 或者直接使用 JDK11/17。

三、解决方案

方案一:升级 JDK(推荐)

如果你使用的是公有 CA 签发的证书(DigiCert、GlobalSign、Let's Encrypt 等),一般不需要手动导入证书
只要 JDK 足够新,并且目标服务器配置了完整证书链,就可以正常使用。


方案二:修复服务器证书链

如果你是目标服务的维护方,确保服务器正确安装了 完整证书链

  • 站点证书

  • 中间证书

  • 根证书(通常不需要手动安装)

Nginx 示例配置:

ssl_certificate     fullchain.pem;   # 包含站点证书 + 中间证书
ssl_certificate_key privkey.pem;

方案三:导入证书到 JDK 信任库(自签名或内部 CA)

如果必须调用自签名 HTTPS 服务,可以手动导入证书:

keytool -import -alias myserver \
  -keystore $JAVA_HOME/jre/lib/security/cacerts \
  -file server.crt

默认密码:changeit


方案四:跳过 SSL 验证(仅限开发测试)

在测试环境,可以让 RestTemplate 忽略证书校验:

public static RestTemplate insecureRestTemplate() throws Exception {
    TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType) {}
            public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
        }
    };

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    factory.setHttpClient(HttpClients.custom()
            .setSSLContext(sslContext)
            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
            .build());

    return new RestTemplate(factory);
}

四、总结

  • 如果是 公有 CA 签发的证书,通常不需要额外配置,只要:

    • JDK 版本足够新

    • 目标服务器安装了完整证书链
      就能解决。

  • 如果是 内部/自签名证书,需要手动导入到 JDK 信任库。

  • 开发环境临时调试,可以使用 跳过 SSL 验证的 RestTemplate,但千万不要在生产使用