天天看點

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