Java调用REST API时出现SSLHandshakeException异常求助
解决Java调用REST API时的SSLHandshakeException异常
嘿,遇到这个异常别慌!SSLHandshakeException本质就是你的Java程序和目标API服务器在建立加密连接(SSL/TLS)时“谈崩了”——双方没法就加密规则、证书信任达成一致,导致连接建不起来。我结合你代码里提到的两种调用方式(HttpURLConnection和JAX-RS Client),给你分场景讲解决方案:
先搞懂常见触发原因
这个异常最常出现的几个场景:
- 目标API用了自签名SSL证书(这类证书不在JVM默认的信任证书库
cacerts里,JVM会直接拒绝信任) - 你的JVM版本太旧,自带的信任证书库没有服务器CA根证书(比如某些新的CA证书没被老版本Java收录)
- 服务器禁用了你的Java版本支持的SSL协议(比如Java 8默认支持TLS 1.0/1.1/1.2,而服务器只允许TLS 1.3)
分场景解决方法
场景1:用HttpURLConnection调用API(测试环境临时方案)
如果是测试环境,不想折腾证书导入,可以临时跳过SSL验证(注意:生产环境绝对不能这么做,会带来严重安全风险!):
先写一个工具类来禁用SSL验证:
import javax.net.ssl.*; import java.security.cert.X509Certificate; public class SSLUtil { public static void disableSSLVerification() throws Exception { // 创建一个信任所有证书的TrustManager TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) {} @Override public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; // 初始化SSL上下文,设置信任管理器 SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // 跳过主机名验证(避免证书域名和请求域名不一致的报错) HostnameVerifier allHostsValid = (hostname, session) -> true; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); } }
然后在你的请求代码前调用这个工具方法:
public static void main(String[] args) throws Exception { // 临时禁用SSL验证(仅测试用) SSLUtil.disableSSLVerification(); URL url = new URL("https://your-target-api-url.com"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 后续的请求配置(比如请求方法、请求体等) // ... }
场景2:用JAX-RS ClientBuilder调用API(测试环境临时方案)
如果你用的是JAX-RS的客户端(比如Jersey或RESTEasy),可以这么配置信任所有证书:
import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.core.MediaType; import java.security.cert.X509Certificate; public class ApiClient { public static Client createTrustAllClient() throws Exception { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) {} @Override public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }, new java.security.SecureRandom()); return ClientBuilder.newBuilder() .sslContext(sslContext) .hostnameVerifier((hostname, session) -> true) .build(); } public static void main(String[] args) throws Exception { Client client = createTrustAllClient(); // 调用API并获取响应 String response = client.target("https://your-target-api-url.com") .request(MediaType.APPLICATION_JSON) .get(String.class); System.out.println(response); } }
生产环境的正确姿势
上面的临时方案只能用于测试!生产环境必须用安全的方式处理:
- 获取目标服务器的SSL证书:可以通过浏览器访问API地址,导出证书(通常是.crt格式)
- 将证书导入JVM信任库:使用
keytool命令导入到JVM默认的cacerts库中,示例:
默认的keytool -import -alias api-server-cert -file /path/to/your/cert.crt -keystore $JAVA_HOME/jre/lib/security/cacertscacerts密码是changeit - 自定义信任库(可选):如果不想修改全局的
cacerts,可以创建自己的信任库,然后在代码中指定:import org.apache.http.ssl.SSLContexts; // 这里用Apache的SSLContexts工具类,也可以用JDK原生方式 SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new File("/path/to/your/custom-truststore.jks"), "your-truststore-password".toCharArray()) .build(); Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
内容的提问来源于stack exchange,提问作者ROM
相关产品推荐
相关产品推荐

