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);
查看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; } } }
二、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; } }
三、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]; } } }
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