前言
正常情况下,Java使用httpclient访问http或者https请求都是没有问题的,但是如果https请求的证书是未经过认证的,就会报错
Exception in thread "main" 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
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
at it.aspirin.http.HttpDemo.doGet1(HttpDemo.java:53)
at it.aspirin.http.HttpDemo.main(HttpDemo.java:31)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:262)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621)
... 21 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 27 more
本文就此问题展开
正文
要解决上述问题有两个方案,跳过https的ssl证书验证或者将https证书安装到本地。如果访问未知网站,为了安全,建议采用后者。
由于我知道该https连接来自哪里,所以直接采用跳过ssl证书验证的方式。而且我发现网上博客大多讲后者,如有需要大家也可以自行百度。
方式一
就第一个方案这里提供两种方式,第一种方式需要写的内容比较多,但是比较灵活。
跳过ssl证书验证,只需要在创建HTTPClient的时候设置SSLConnectionSocketFactory信任所有证书即可。
//绕过ssl证书验证
public static CloseableHttpClient wrapClient() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager trustManager = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
};
ctx.init(null, new TrustManager[]{trustManager}, null);
SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
return HttpClients.custom()
.setSSLSocketFactory(ssf)
.build();
} catch (Exception e) {
return HttpClients.createDefault();
}
}
此时再使用httpclient发送不安全的https请求就可以正常访问了
public static void doGet1(String url) throws IOException {
HttpGet httpGet = new HttpGet("https://xxxx");
//设置cookie
httpGet.setHeader("Cookie", "JSSENSIONID=xxxxxxxxxxxxx");
CloseableHttpResponse response = httpClient.execute(httpGet);
System.out.println(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
}
方式二
Java有个工具包,叫hutool,里面有封装HTTP请求,在访问不安全的https请求的时候可以自动跳过ssl认证。如果需要实现的功能比较简单,没有性能等要求,使用该方式比较简单。
引入hutool依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.2</version>
</dependency>
使用hutool里面的HTTPClient工具栏发送HTTP请求
String str = HttpUtil.get("https://xxxxx");