用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