天天看点

RestTemplate调用https接口报错:Caused by: javax.net.ssl.SSLHandshakeException

用RestTemplate调用http接口没问题,但是调用https接口(post请求)时报错。

报错信息:

Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://xxx.xxx.xxx/oauth2/access_token": Received fatal alert: handshake_failure; nested exception is javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:607)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
	at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:384)
	at com.taikang.udp.common.util.RestUtil.main(RestUtil.java:103)
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
	at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1979)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1086)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
	at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
           

原因:证书验证、协议版本问题。HTTPS请求 = 超文本传输协议HTTP + 安全套接字层SSL。

项目使用的是jdk1.7。jdk1.7默认支持的TLS协议是v1 ,jdk1.8默认支持的是v1.2。

解决方法:创建RestTemplate时,修改协议版本。

用一个SimpleClientHttpRequestFactory的实现类来修改协议版本:

public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {

    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
        try {
            if (!(connection instanceof HttpsURLConnection)) {// http协议
                //throw new RuntimeException("An instance of HttpsURLConnection is expected");
                super.prepareConnection(connection, httpMethod);
            }
            if (connection instanceof HttpsURLConnection) {// https协议,修改协议版本
                SSLContext ctx = SSLContext.getInstance("TLSv1.2");
                X509TrustManager tm = new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain,
                                                   String authType) throws CertificateException {
                    }
                    @Override
                    public void checkServerTrusted(X509Certificate[] chain,
                                                   String authType) throws CertificateException {
                    }
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                };
                ctx.init(null, new TrustManager[]{tm}, null);
                org.apache.http.conn.ssl.SSLSocketFactory ssf = new org.apache.http.conn.ssl.SSLSocketFactory(ctx, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                ((HttpsURLConnection) connection).setSSLSocketFactory(ctx.getSocketFactory());
                HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;

                super.prepareConnection(httpsConnection, httpMethod);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
           

调用接口部分代码:

String url = "https://xxx.xxx.xxx.com/ruleapi/rule/xxx";
        RestTemplate client = new RestTemplate(new HttpsClientRequestFactory());
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);// 提交方式都是表单提交
        headers.add("access_token","acf6e8dce7267deaf4d15260f77e68d4");// 把获取access_token返参中的access_token放到header中
        // 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
        MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
        params.add("xxx", "xxx");// 外部机构系统来源,放在Body中
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, headers);
        // 执行请求
        ResponseEntity<String> response = client.exchange(url, HttpMethod.POST, requestEntity, String.class);
           

参考:

https://blog.csdn.net/u014430366/article/details/65633679

https://blog.csdn.net/zdyueguanyun/article/details/89004650

https://blog.csdn.net/justry_deng/article/details/82531306