以前没做过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
|
public static String HttpsSingletonGetRequest(String requestUrl, Map<String,String> paramMap) throws Exception{ StringBuffer resultMsg; CloseableHttpClient httpClient = getSingletonHttpsClient(); 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); 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(); }
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(); } }
private static CloseableHttpClient getSingletonHttpsClient() { RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create(); ConnectionSocketFactory socketFactory = new PlainConnectionSocketFactory(); registryBuilder.register("http", socketFactory); try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); TrustStrategy anyTrustStrategy = (x509Certificates, s) -> true; HostnameVerifier verifier = (s, sslSession) -> true; SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, anyTrustStrategy).build(); LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, verifier); 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);
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
| 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(); } } }
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(); }
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,我将不胜感激!