纠结了一天的问题终于落下了帷幕!先听一首歌吧
今天使用了一些httpclient包进行https网页数据的访问,但是一直返回403的问题,一开始以为网站做了限制为了防止爬虫,后来就加入了头部user-Agent来模拟浏览器,结果还是不行。紧接着又加入了cookie,结果仍然返回403。直到下午去github上看到了一个二次封装httpclient的util工具。下载下来放到了idea里访问了一下https的这个url结果成功了。对比了一下不同之处,除了httpclient jar包的版本有高低外还有就是使用的jdk版本不同。于是将相同版本的httpclient jar包放到新建的测试项目下,同样使用jdk1.6 ,结果还是不行。此时就定位到问题的所在了,原来是jdk的版本导致的问题。紧接着在百度搜所了一下原因找到了以下的文章:
问题描述:访问https出现hostname in certificate didn't match问题,本地测试正常原因是本地环境支持了SNI(Server Name Indication),虚拟主机大力发展起来,造成了一个IP会对应多个域名的情况,SNI就是专门用于解决这个问题,它允许客户端在发起SSL握手请求时,就提交请求的Host信息,使得服务器能够切换到正确的域并返回相应的证书。
在java客户端上,SNI要求JDK至少到
,HttpClient至少到4.3.2,本地测试环境满足该要求,而线上环境JDK是1.6的所以会有问题。
解决办法
1,升级运行环境到满足SNI的要求
2,选择忽略hostname校验
可以创建X509HostnameVerifier,重载verify(String hostname, SSLSession session)方法返回true,并设置到httpclient,用于https请求。
新建MyHttpsClient 类
public class MyHttpsClient {
public static MyHttpsClient getInstance(){
return new MyHttpsClient();
}
public CloseableHttpClient createHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException{
SSLContextBuilder builder = SSLContexts.custom();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// TODO Auto-generated method stub
return true;
}
});
SSLContext sslContext = builder.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext, new X509HostnameVerifier() {
@Override
public void verify(String arg0, SSLSocket arg1)
throws IOException {
// TODO Auto-generated method stub
}
@Override
public void verify(String arg0, X509Certificate arg1)
throws SSLException {
// TODO Auto-generated method stub
}
@Override
public void verify(String arg0, String[] arg1, String[] arg2)
throws SSLException {
// TODO Auto-generated method stub
}
@Override
public boolean verify(String hostname, SSLSession session) {
// TODO Auto-generated method stub
return true;
}
});
Registry socketFactoryRegistry = RegistryBuilder
. create().register("https", sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
socketFactoryRegistry);
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(cm).build();
return httpclient;
}
}
在HttpUtil中,使用 CloseableHttpClient httpclient = MyHttpsClient.getInstance().createHttpClient();
领取专属 10元无门槛券
私享最新 技术干货