002-03-RestTemplate-实现httpclient、okhttp忽略证书,以及使用javax SSLContext证书ssl使用

1、RestTemplate相关

能够参看地址流程,查看doExecute核心代码以下html

//经过ClientHttpRequestFactory工厂生产一个ClientHttpRequest 
ClientHttpRequest request = this.createRequest(url, method);//第一步
if(requestCallback != null) { //封装了请求头和请求体,使用了HttpMessageConverter
 requestCallback.doWithRequest(request); } //ClientHttpRequest&execute 执行请求
response = request.execute(); //response error处理
this.handleResponse(url, method, response); if(responseExtractor == null) { resource = null; return resource; } var14 = responseExtractor.extractData(response);
View Code

 

查看ClientHttpRequestFactory请求工厂接口,以及具体实现类java

  

  经过查看import以及源码、基于HttpComponents、Netty和OkHttp的涉及到了ssl。git

  几个request factory均可以经过constuctor注入相应client实例,对于HttpComponents/Netty/OkHttp添加ssl,内部都指向了一个class:SSLContext。github

 2、证书相关

  参看文章:http://www.cnblogs.com/bjlhx/category/980088.htmlweb

  微信证书算法

  

  微信给的商户证书是文件,要读取成程序运行时的数据、因此就用到了FileInputStream和KeyStore。 看javadoc,KeyStore和KeyStoreSpi做用是统一封装存放了不一样算法的证书密钥,spring

 3、测试类

3.一、包依赖

 http请求项目搭建:地址https://github.com/bjlhx15/common.git 中的spring-framework-core 的 spring-http-XX相关apache

  主要使用:spring-http-02-httpclient的xmlapi

3.二、忽略证书文件检查安全

注意参看demo,测试时候没有特殊增长ssl也能够,使用默认的访问https,一下是在不能使用时须要添加的

一、JDK原生的URLConnection,即SimpleClientHttpRequestFactory

public class HttpsSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { try { if (!(connection instanceof HttpsURLConnection)) { // throw new RuntimeException("An instance of HttpsURLConnection is expected");
                return; } HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); httpsConnection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); super.prepareConnection(httpsConnection, httpMethod); } catch (Exception e) { e.printStackTrace(); } } /** * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section) */
    private static class MyCustomSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException { final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } private Socket overrideProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("An instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"}); return socket; } } }
View Code

二、Apache的Http Client,即HttpComponentsClientHttpRequestFactory

@Configuration public class Config02DeafultpoolHttps { @Bean public RestTemplate getRestTemplate(RestTemplateBuilder builder) throws Exception { RestTemplate restTemplate = builder.build(); restTemplate.setRequestFactory(clientHttpRequestFactory()); // 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters(); Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator(); while (iterator.hasNext()) { HttpMessageConverter<?> converter = iterator.next(); if (converter instanceof StringHttpMessageConverter) { iterator.remove(); } } messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8"))); return restTemplate; } @Bean public HttpClientConnectionManager poolingConnectionManager() throws Exception { // 在调用SSL以前须要重写验证方法,取消检测SSL
        X509TrustManager trustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] xcs, String str) {} @Override public void checkServerTrusted(X509Certificate[] xcs, String str) {} }; SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS); ctx.init(null, new TrustManager[] { trustManager }, null); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https",socketFactory).build(); PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); poolingConnectionManager.setMaxTotal(1000); // 链接池最大链接数
        poolingConnectionManager.setDefaultMaxPerRoute(100); // 每一个主机的并发
        return poolingConnectionManager; } @Bean public HttpClientBuilder httpClientBuilder() throws Exception { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //设置HTTP链接管理器
 httpClientBuilder.setConnectionManager(poolingConnectionManager()); return httpClientBuilder; } @Bean public ClientHttpRequestFactory clientHttpRequestFactory() throws Exception { HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setHttpClient(httpClientBuilder().build()); clientHttpRequestFactory.setConnectTimeout(6000); // 链接超时,毫秒
        clientHttpRequestFactory.setReadTimeout(6000); // 读写超时,毫秒
        return clientHttpRequestFactory; } }
View Code

三、okhttp,即OkHttp3ClientHttpRequestFactory

@Configuration public class Config01DefaultHttps { @Bean public RestTemplate getRestTemplate() { return new RestTemplate(new OkHttp3ClientHttpRequestFactory(getokhttpclient())); } public OkHttpClient getokhttpclient() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts()); return builder.build(); } private SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory ssfFactory = null; try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, new TrustManager[]{new TrustAllCerts()}, null); ssfFactory = sc.getSocketFactory(); } catch (Exception e) { } return ssfFactory; } public class TrustAllCerts implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }
View Code

3.三、证书文件RestTemplate的factory

import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import okhttp3.OkHttpClient; 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.ssl.SSLContexts; import org.springframework.beans.factory.DisposableBean; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.Netty4ClientHttpRequestFactory; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.RestTemplate; import javax.net.ssl.*; import java.io.FileInputStream; import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.SecureRandom; public class RestTemplateExample { private static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; public static void main(String[] args) throws Exception { final String certFile = args[0]; // cert file location
        final String passwd = args[1]; // mch_id
        KeyStore keyStore = loadFrom("PKCS12", certFile, passwd); // httpComponent
        ClientHttpRequestFactory factory = createHttpComponentFactory(keyStore, passwd); testGet(factory); // okhttp
        factory = createOkHttp3Factory(keyStore, passwd); testGet(factory); // netty
        factory = createNettyFactory(keyStore, passwd); testGet(factory); ((DisposableBean) factory).destroy(); System.out.println("end"); } private static void testGet(ClientHttpRequestFactory factory) { RestTemplate restTemplate = new RestTemplate(factory); System.out.println("using " + restTemplate.getRequestFactory().getClass()); restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8)); ResponseEntity<String> getRes = restTemplate.getForEntity(REFUND_URL, String.class); System.out.println(getRes.getBody()); } private static KeyStore loadFrom(String type, String fileName, String passwd) throws Exception { KeyStore keyStore = KeyStore.getInstance(type); try (FileInputStream fileIn = new FileInputStream(fileName)) { keyStore.load(fileIn, passwd.toCharArray()); } System.out.println("keystore entries: " + keyStore.size()); return keyStore; } private static ClientHttpRequestFactory createOkHttp3Factory(KeyStore keyStore, String passwd) throws Exception { KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, passwd.toCharArray()); SSLContext context = SSLContext.getInstance("TLSV1"); context.init(keyManagerFactory.getKeyManagers(), null, null); OkHttpClient okHttpClient = new OkHttpClient.Builder() .sslSocketFactory(context.getSocketFactory(), getDefaultX509TrustManager()) .build(); return new OkHttp3ClientHttpRequestFactory(okHttpClient); } /** * @see OkHttpClient.Builder#sslSocketFactory(SSLSocketFactory) * @see OkHttpClient.Builder#sslSocketFactory(SSLSocketFactory, X509TrustManager) * @see sun.security.ssl.SSLContextImpl#engineInit(KeyManager[], TrustManager[], SecureRandom) */
    private static X509TrustManager getDefaultX509TrustManager() throws Exception { TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); factory.init((KeyStore) null); return (X509TrustManager) factory.getTrustManagers()[0]; } /** * @see <a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientCustomSSL.java"> * HttpClient custom ssl example</a> */
    private static ClientHttpRequestFactory createHttpComponentFactory(KeyStore keyStore, String passwd) throws Exception { SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, passwd.toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf).build(); return new HttpComponentsClientHttpRequestFactory(httpclient); } private static ClientHttpRequestFactory createNettyFactory(KeyStore keyStore, String passwd) throws Exception { SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, passwd.toCharArray()); SslContext sslContext = sslContextBuilder.keyManager(keyManagerFactory).build(); Netty4ClientHttpRequestFactory factory = new Netty4ClientHttpRequestFactory(); factory.setSslContext(sslContext); return factory; } }

上面写不下的注释: 
1. OkHttp部分,按它javadoc的意思,它须要一个X509TrustManager来处理cert chain,虽然SSLSocketFactory的实现类里就包着一个、但由于没有public get方法、要拿只能靠反射;为了避免用反射使源码变得难看,就只好请开发者在client端调用时传一个进来;即便这样也仍是很难看、且自行导入的CA列表也可能不安全、因此javadoc里也不建议这么作…… 
2. HttpClient包下很多ssl相关的class都被deprecate了,参考的apache官方示例(就是微信例程用法…)稍微改了下。 

 

原文地址:

https://blog.csdn.net/silvita/article/details/70804198

相关文章
相关标签/搜索