简介

本文使用的Apache HttpClient版本为4.5.6.

在使用Apache HttpClient执行https请求时,有时会遇到ssl相关的异常,这里介绍如何通过HttpClient执行https请求。

这里有2种方式:忽略ssl证书并信任任意连接 和 导入证书到秘钥库。

忽略ssl证书并信任任意连接

ssl初始化时需要证书管理器(TrustManager),我们这里使用了一个实现了X509TrustManager的证书管理器。它不做证书的验证,并信任任意的连接。
然后通过HttpClients的setSSLSocketFactory()设置SSLSocketFactory即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class HttpClientFactory {
private final static CloseableHttpClient HTTP_CLIENT;
static {
ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
LayeredConnectionSocketFactory sslsf = sslConnectionSocketFactory();
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainsf)
.register("https", sslsf)
.build();
connManager = new PoolingHttpClientConnectionManager(registry);
// 设置最大连接数
connManager.setMaxTotal(1000);
// 设置每个连接的路由数
connManager.setDefaultMaxPerRoute(100);
// 定时关闭无效的连接
new IdleConnectionMonitorThread(connManager).start();

HTTP_CLIENT = HttpClients.custom()
.setUserAgent(userAgent)
.setSSLSocketFactory(sslsf)
.setConnectionManager(connManager)
.build();
}

/**
* 获取Http客户端连接对象
*
* @return Http客户端连接对象
*/
public final static CloseableHttpClient getCloseableHttpClient() {
return HTTP_CLIENT;
}

private static class HttpsTrustManager implements X509TrustManager {

@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// TODO Auto-generated method stub

}

@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// TODO Auto-generated method stub

}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}

}

private static SSLConnectionSocketFactory sslConnectionSocketFactory() {
return new SSLConnectionSocketFactory(sslContext(),
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
}

private static SSLContext sslContext() {
SSLContext ctx = null;
try {
ctx = SSLContexts.custom().useSSL().build();
ctx.init(null, new TrustManager[] { new HttpsTrustManager() }, new SecureRandom());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
return ctx;
}
}

然后通过getCloseableHttpClient()拿到CloseableHttpClient,就可以执行https请求了(与http请求一样)。
比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static String getPageContent(String url,String charset) {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = null;
try {
response = getCloseableHttpClient().execute(httpGet);
return EntityUtils.toString(response.getEntity(), charset == null ? "UTF-8" : charset);
} catch (IOException e) {
LOGGER.error("解析路径异常 url = " + url,e);
} finally {
close(httpGet, response);
}
return null;
}

导入证书到秘钥库

如果已经有证书,我们将证书导入秘钥库即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class HttpClientFactory {

private static CloseableHttpClient client;

public static HttpClient getHttpsClient() throws Exception {

if (client != null) {
return client;
}
SSLContext sslcontext = getSSLContext();
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
client = HttpClients.custom().setSSLSocketFactory(factory).build();

return client;
}

public static void releaseInstance() {
client = null;
}

private SSLContext getSSLContext() throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File("my.keystore"));
try {
trustStore.load(instream, "nopassword".toCharArray());
} finally {
instream.close();
}
return SSLContexts.custom()
.loadTrustMaterial(trustStore)
.build();
}
}

其实这2种方式也只有创建SSLContext不同。参考文章也说了生产环境还是建议使用第2种。

文章参考:https://prasans.info/2014/06/making-https-call-using-apache-httpclient/

如果是使用Java的HttpClientConnection,则可以参考:http://www.devsumo.com/technotes/2014/01/java-trusting-https-server-certificates/