堆栈错误先贴出来:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for “https的url地址”: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification p
我当时的情况是自己的开发环境可以请求到对方的https地址,但是发布到Linux的测试环境后请求不通,报错如上.解决回顾后,觉得当时做的无用功挺多,现在想来按以下步骤确认解决问题会节省很多时间.
1.环境确认
一定要先确定自己的环境中的防火墙,代理,配置正确.从整个大局上分析解决问题.
可以使用curl -k (跳过证书)命令,wget命令等测试自己请求的https地址是否可以在环境中连通.我是使用wget命令提示访问被拒绝
2.生成证书,导入证书或安装证书
1.生成证书,上传到Linux上,并在代码中引用:
https://blog.youkuaiyun.com/i_like1/article/details/80334298
2.从浏览器中下载对应证书,并上传到Linux找那个的jdk环境中:
https://blog.youkuaiyun.com/liangcha007/article/details/84661532
3.代码层面改造
- restTemplate请求方式
使用百度到最多的答案SslUtils.ignoreSsl();后,请求出现400错误.参照了这片博文后https://blog.youkuaiyun.com/Petershusheng/article/details/78337620指定了对应requestFactory后解决问题.
restTemplate代码:
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
SslUtils.ignoreSsl();
ResponseEntity<Res> resEntity = restTemplate.postForEntity(url, entity, Res.class);
SslUtils代码:
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class SslUtils {
public static void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new miTM();
trustAllCerts[0] = tm;
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
static class miTM implements TrustManager, X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(X509Certificate[] certs) {
return true;
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
}
/**
* 忽略HTTPS请求的SSL证书,必须在openConnection之前调用
*
* @throws Exception
*/
public static void ignoreSsl() throws Exception {
HostnameVerifier hv = new HostnameVerifier() {
@Override
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
};
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
}
- httpsClient请求(未采纳)
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSON;
Res res = null;
CloseableHttpClient httpsClient = null;
try {
httpsClient = (CloseableHttpClient) HttpClientFactoryWithNoSSL.getHttpsClient();
} catch (Exception e) {
e.printStackTrace();
}
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Content-Type", "application/json");
String mem = "";
CloseableHttpResponse responseMem = null;
CloseableHttpResponse responseCPU = null;
CloseableHttpResponse responseDisk = null;
try {
responseMem = httpsClient.execute(httpGet);
if (responseMem.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
mem = "0";
} else {
org.apache.http.HttpEntity resEntity = responseMem.getEntity();
if (resEntity != null) {
mem = EntityUtils.toString(resEntity, "UTF-8");
res = JSON.parseObject(mem,Res.class);
}
EntityUtils.consume(resEntity);//要消耗消息实体,这是必须步骤
}
} catch (Exception e1) {
e1.printStackTrace();
mem = "0";
} finally {
try {
if(responseMem != null){
responseMem.close();//尝试关闭资源,将socket连接返回给资源池
}
} catch (IOException e) {
e.printStackTrace();
}
}
gradle引入信息,可自行转换成maven格式
compile('org.apache.httpcomponents:httpclient:4.5.3')
所需的HttpClientFactoryWithNoSSL类
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import com.walmart.workwechat.manager.HttpsTrustManager;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
public class HttpClientFactoryWithNoSSL {
private static CloseableHttpClient client;
//连接池
private static HttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
/**
* 获取不需要ssl认证的httpClient实例
*
* @Title: getHttpsClientWithNoCert
* @Description: TODO
* @Author: Think
* @Date :Jan 16, 2019
* @return: HttpClient
*/
public static HttpClient getHttpsClient() throws Exception {
if (client != null) {
return client;
}
SSLContext sslcontext = SSLContexts.custom().useSSL().build();
sslcontext.init(null, new X509TrustManager[]{new HttpsTrustManager()}, new SecureRandom());
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client = HttpClients.custom().setConnectionManager(poolingConnManager).setSSLSocketFactory(factory).build();
return client;
}
public static void releaseInstance() {
client = null;
}
}
- httpsClient2请求
import javax.net.ssl.SSLContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
WeChatTokenRes res = null;
SSLContext sslcontext = null;
try {
//设置协议http和https对应的处理socket链接工厂的对象
sslcontext = createIgnoreVerifySSL();
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslcontext))
.build();
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
HttpClients.custom().setConnectionManager(connManager);
//创建自定义的httpclient对象
CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
//处理请求参数拼接(参数为json) 如:?name = ""&pwd = ""
String urlNameString = url;
//创建get方式请求对象
HttpGet get = new HttpGet(urlNameString);
//指定报文头Content-type、User-Agent
//get.setHeader("Content-type", "application/x-www-form-urlencoded");
get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:6.0.2) Gecko/20100101 Firefox/6.0.2");
//执行请求操作,并拿到结果(同步阻塞)
CloseableHttpResponse response = client.execute(get);
//获取结果实体
org.apache.http.HttpEntity entity = response.getEntity();
if (entity != null) {
//按指定编码转换结果实体为String类型
String body = EntityUtils.toString(entity, "UTF-8");
res = JSON.parseObject(body,WeChatTokenRes.class);
// 返回对外IP信息
res.setMessage(body);
}
EntityUtils.consume(entity);
//释放链接
response.close();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}finally{
}
return res;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sc = SSLContext.getInstance("SSLv3");
// 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
String paramString) {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
String paramString) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sc.init(null, new TrustManager[] { trustManager }, null);
return sc;
}
4.总结
以上从环境,证书,代码三个方面解决报错问题.我最后使用的restTemplate方式请求通过的.以上经验供借鉴参考.