以前没做过Https的相关的业务,所以突然来一个Https的调接口任务,特别棘手,项目还比较急。
现在项目已经趋于稳定,特来总结

Https 是什么

Https是一种认证方式,是在Http协议基础上添加了SSL加密协议,采用https的服务器必须从CA (Certificate Authority)申请一个用于证明服务器用途类型的证书。该证书只有用于对应的服务器的时候,客户端才信任此主机。

Https 长什么样

你可以使用命令行手段生成一个CA,这种本地CA并没有公信力,但我们学习足够用了
基于OpenSSL命令行工具,我们可以很方便的创建CA并签发证书,另外一种方式是从CA认证服务商家处,提供信息,由服务商签发证书。
Https接口方面,还有单向和双向区分,单向证书只需要客户端信任主机,即可正常访问。
双向认证比较麻烦,需要我们本地也生成一个CA,签发证书交给服务端,服务端认证你的证书。并提供他的证书,最终拿到一个jks文件

Https 单向 API

如果想调用Https 单向认证的API,这个好办,我们只需在Java层面,所有证书都选择信任,或加入到java的信任库中
这里我介绍的是Java调用的方式,添加信任库的方法网上很多。

废话不说上代码:

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
* 单向Https请求入口
* @return
*/
public static String HttpsSingletonGetRequest(String requestUrl, Map<String,String> paramMap) throws Exception{
StringBuffer resultMsg;
//封装Client
CloseableHttpClient httpClient = getSingletonHttpsClient();
try {
//加载URL
StringBuffer url = new StringBuffer().append(requestUrl);
//加载参数
if (!Objects.isNull(paramMap)){
for (String key : paramMap.keySet()){
url.append("&").append(key).append("=").append(paramMap.get(key));
}
}

String realUrl = url.toString().replaceFirst("&", "?");
HttpGet httpGet = new HttpGet(realUrl);
httpGet.addHeader("Content-Type", "application/json;encoding=utf-8");
CloseableHttpResponse response = httpClient.execute(httpGet);
//发起请求方法
resultMsg = send(response);
} finally {
httpClient.close();
}
return resultMsg == null ? null : resultMsg.toString();
}

/**
* 发起请求 相信都熟悉HttpClient这个库,我就不废话了
* @return
*/
private static StringBuffer send(CloseableHttpResponse response) throws IOException {
try {
StringBuffer resultMsg = null;
HttpEntity entity = response.getEntity();
if (entity != null) {
resultMsg = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
String text;
while ((text = bufferedReader.readLine()) != null) {
resultMsg.append(text);
}
}

EntityUtils.consume(entity);

if (resultMsg == null){
throw new RuntimeException("network response error at HttpsClientUtil send()");
}
return resultMsg;
} finally {
response.close();
}
}

/**
* 核心: 生成Client方法
* @return
*/
private static CloseableHttpClient getSingletonHttpsClient() {
//首先构建一个注册工厂 用来注册http/https请求
RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
ConnectionSocketFactory socketFactory = new PlainConnectionSocketFactory();
//注册http请求没什么好说的
registryBuilder.register("http", socketFactory);
//指定信任密钥存储对象和连接套接字工厂
try {
//实例化一个KeyStore,也就是认证库
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
//信任所有
TrustStrategy anyTrustStrategy = (x509Certificates, s) -> true;
//信任所有host
HostnameVerifier verifier = (s, sslSession) -> true;
//KeyStore&TrustStrategy加载ssl上下文
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, anyTrustStrategy).build();
//此方法是生成一个Socket工场,https请求在真实请求接口前,需提前发起Socket请求验证,参数是ssl上下文以及信任所有host的方法
LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, verifier);
//注册Https请求方式
registryBuilder.register("https", sslSF);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
} catch (KeyManagementException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}

Registry<ConnectionSocketFactory> registry = registryBuilder.build();
//设置连接管理器
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry);

//返回生成的CloseableHttpClient
return HttpClientBuilder.create().setConnectionManager(connManager).build();
}

以上我们就可以访问Https单向加密的接口或网址了,比如https://www.baidu.com

Https 双向 API

这个相对单向来说,概念上复杂一些,我也并没有完全搞懂,所以概念上的就不聊太多了。

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
73
74
75
//首先我们要初始化一个SSLConnectionSocketFactory
private static SSLConnectionSocketFactory initConfig() throws Exception {
KeyStore keyStore = KeyStore.getInstance("jks");
InputStream in = null;
try {
in = new FileInputStream(TRUST_STORE_FILE);
keyStore.load(in, "bld365".toCharArray());
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(keyStore, new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, "bld365".toCharArray())
.build();
HostnameVerifier verifier = (s, sslSession) -> true;
return new SSLConnectionSocketFactory(
sslcontext,
new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"},
null,
verifier
);
} catch (Exception e) {
e.printStackTrace();
throw new Exception("初始化client keyStore 失败:" + e.getMessage());
} finally {
if (null != in) {
in.close();
}
}
}

//然后通过initConfig初始化CloseableHttpClient完成请求
public static String HttpsGetRequest(String requestUrl, Map<String,String> paramMap) throws Exception{
StringBuffer resultMsg;
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(initConfig()).build();
try {
StringBuffer url = new StringBuffer().append(requestUrl);

if (!Objects.isNull(paramMap)){
for (String key : paramMap.keySet()){
url.append("&").append(key).append("=").append(paramMap.get(key));
}
}

String realUrl = url.toString().replaceFirst("&", "?");
HttpGet httpGet = new HttpGet(realUrl);
CloseableHttpResponse response = httpClient.execute(httpGet);
resultMsg = send(response);
} finally {
httpClient.close();
}
return resultMsg == null ? null : resultMsg.toString();
}

//最后调用send方法发送请求
private static StringBuffer send(CloseableHttpResponse response) throws IOException {
try {
StringBuffer resultMsg = null;
HttpEntity entity = response.getEntity();
if (entity != null) {
resultMsg = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
String text;
while ((text = bufferedReader.readLine()) != null) {
resultMsg.append(text);
}
}

EntityUtils.consume(entity);

if (resultMsg == null){
throw new RuntimeException("network response error at HttpsClientUtil send()");
}
return resultMsg;
} finally {
response.close();
}
}

这里我只拿Get请求做了演示,其实Post是一样的,核心操作都在CloseableHttpClient这个对象实例化的过程发生。
看网上并没有新的解决办法,很多接口都被弃用,甚至是不能用,所以在自己博客里献丑,如果有读者发现有什么不对的话,请点击博客主页Github地址提出Issus,我将不胜感激!