【网络协议】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调用的方式,添加信任库的方法网上很多。

废话不说上代码:

	/**
     * 单向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

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

	//首先我们要初始化一个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,我将不胜感激!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你向着阳光奔跑的背影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值