用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