1.简述
使用Https请求知道链接时出现javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure异常,是因为ssl协议错误。
2.解决方案
主要是在创建SSLContext的时候指定TLS协议,就可以解决这个问题,使用的是httpclient-4.5.jar、httpcore-4.4.1.jar。
出现异常示例代码如下:

import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; public class Demo { public static void main(String[] args) throws Exception { String url = "https://weather.com/weather/today/l/22.69,113.91?par=google"; String result = ""; CloseableHttpClient httpClient = null; try { SSLContextBuilder builder = new SSLContextBuilder(); // 全部信任 不做身份鉴定 builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return true; } }); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new String[] { "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2" }, null, NoopHostnameVerifier.INSTANCE); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create().register("http", new PlainConnectionSocketFactory()).register("https", sslsf).build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(200);// max connection httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(cm).setConnectionManagerShared(true).build(); HttpGet httpGet = new HttpGet(url); RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();// 设置请求和传输超时时间 httpGet.setConfig(requestConfig); HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity resEntity = httpResponse.getEntity(); result = EntityUtils.toString(resEntity); } catch (Exception e) { throw e; } finally { if (httpClient != null) { httpClient.close(); } } System.out.println(result); } }
第一种成功获取的示例代码如下:

import java.io.IOException; import java.net.URI; import javax.net.ssl.SSLContext; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.util.EntityUtils; public class Demo { public static void main(String[] args) throws Exception { String url = "https://weather.com/weather/today/l/22.69,113.91?par=google"; String result = null; HttpGet get = new HttpGet(); CloseableHttpResponse res = null; CloseableHttpClient client = null; try { RequestConfig config = RequestConfig.custom().setConnectTimeout(10000).setSocketTimeout(12000).build(); SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null,null,null); SSLContext.setDefault(sslContext); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslContext)).build(); PoolingHttpClientConnectionManager mananger = new PoolingHttpClientConnectionManager(socketFactoryRegistry); mananger.setMaxTotal(100); mananger.setDefaultMaxPerRoute(20); client = HttpClients.custom().setConnectionManager(mananger).build(); get.setConfig(config); get.setURI(new URI(url)); res = client.execute(get); result = EntityUtils.toString(res.getEntity()); } catch (Exception e) { e.printStackTrace(); }finally { get.releaseConnection(); try { res.close(); client.close(); } catch (IOException e1) { e1.printStackTrace(); } } System.out.println(result); } }
第二种成功获取的示例代码如下:

import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; public class Demo { public static void main(String[] args) throws Exception { String url = "https://weather.com/weather/today/l/22.69,113.91?par=google"; String result = ""; CloseableHttpClient httpClient = null; try { SSLContextBuilder builder = new SSLContextBuilder(); // 全部信任 不做身份鉴定 builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { return true; } }); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new String[] { "TLSv1", "TLSv1.2" }, null, NoopHostnameVerifier.INSTANCE); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create().register("http", new PlainConnectionSocketFactory()).register("https", sslsf).build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(200);// max connection httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(cm).setConnectionManagerShared(true).build(); HttpGet httpGet = new HttpGet(url); RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();// 设置请求和传输超时时间 httpGet.setConfig(requestConfig); HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity resEntity = httpResponse.getEntity(); result = EntityUtils.toString(resEntity); } catch (Exception e) { throw e; } finally { if (httpClient != null) { httpClient.close(); } } System.out.println(result); } }
第二种成功是因为去除了SSLv2Hello、SSLv3协议。
注意:三个示例中的最后一个输出记得屏蔽,因为内容较多不屏蔽则会卡一会。