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協議。
注意:三個示例中的最后一個輸出記得屏蔽,因為內容較多不屏蔽則會卡一會。