问题描述:访问https出现hostname in certificate didn't match问题,本地测试正常原因是本地环境支持了SNI(Server Name Indication),虚拟主机大力发展起来,造成了一个IP会对应多个域名的情况,SNI就是专门用于解决这个问题,它允许客户端在发起SSL握手请求时,就提交请求的Host信息,使得服务器能够切换到正确的域并返回相应的证书。
在java客户端上,SNI要求JDK至少到1.7,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<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory> 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();