jiaguoquan00 2020-05-26
在HttpClient 4.x版本中引入了大量的构造器设计模式
https请求建立详解
首先建立一个信任任何密钥的策略。代码很简单,不去考虑证书链和授权类型,均认为是受信任的:
class AnyTrustStrategy implements TrustStrategy{ @Override public boolean isTrusted(X509Certificate[] chain, String authType)throws CertificateException { return true; } }
HttpClient既能处理常规http协议,又能支持https,根源在于在连接管理器中注册了不同的连接创建工厂。当访问url的schema为http时,调用明文连接套节工厂来建立连接;当访问url的schema为https时,调用SSL连接套接字工厂来建立连接。对于http的连接我们不做修改,只针对使用SSL的https连接来进行自定义:
RegistryBuilder registryBuilder = RegistryBuilder.create(); ConnectionSocketFactory plainSF =newPlainConnectionSocketFactory(); registryBuilder.register("http", plainSF); //指定信任密钥存储对象和连接套接字工厂 try{ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore,newAnyTrustStrategy()).build(); LayeredConnectionSocketFactory sslSF =newSSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); registryBuilder.register("https", sslSF); }catch(KeyStoreException e) { thrownewRuntimeException(e); }catch(KeyManagementException e) { thrownewRuntimeException(e); }catch(NoSuchAlgorithmException e) { thrownewRuntimeException(e); } Registry registry = registryBuilder.build();
在上述代码中可以看到,首先建立了一个密钥存储容器,随后让SSLContext开启TLS,并将密钥存储容器和信任任何主机的策略加载到该上下文中。构造SSL连接工厂时,将自定义的上下文和允许任何主机名通过校验的指令一并传入。最后将这样一个自定义的SSL连接工厂注册到https协议上。
//设置连接管理器 PoolingHttpClientConnectionManager connManager =newPoolingHttpClientConnectionManager(registry); connManager.setDefaultConnectionConfig(connConfig); connManager.setDefaultSocketConfig(socketConfig); //构建客户端 HttpClient client= HttpClientBuilder.create().setConnectionManager(connManager).build();
为了让我们的HttpClient具有多线程处理的能力,连接管理器选用了PoolingHttpClientConnectionManager,将协议注册信息传入连接管理器,最后再次利用构造器的模式创建出我们需要的HttpClient。随后的GET/POST请求发起方法http和https之间没有差异。
为了验证我们的代码是否成功,可以做下JUnit单元测试:
@Test publicvoiddoTest()throwsClientProtocolException, URISyntaxException, IOException{ HttpUtil util = HttpUtil.getInstance(); InputStream in = util.doGet("https://kyfw.12306.cn/otn/leftTicket/init"); String retVal = HttpUtil.readStream(in, HttpUtil.defaultEncoding); System.out.println(retVal); }
执行后可以在控制台看到12306余票查询界面的html代码
/** * 基本Post请求 * @param url 请求url * @param queryParams 请求头的查询参数 * @param json 直接放入post请求体中的文本(请使用JSON) * @return * @throws URISyntaxException * @throws UnsupportedEncodingException */ publicHttpResponse doPostBasic(String url, Map queryParams, String json)throwsURISyntaxException, ClientProtocolException, IOException{ HttpPost pm =newHttpPost(); URIBuilder builder =newURIBuilder(url); //填入查询参数 if(MapUtils.isNotEmpty(queryParams)){ builder.setParameters(HttpUtil.paramsConverter(queryParams)); } pm.setURI(builder.build()); //填入post json数据 if(StringUtils.isNotBlank(json)){ //下面的ContentType完整类名为:org.apache.http.entity.ContentType pm.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); } return client.execute(pm); }
public void initSSLConfigForTwoWay() throws Exception { HttpClientBuilder b = HttpClientBuilder.create(); // 1 Import your own certificate // String demo_base_Path = System.getProperty("user.dir"); // String demo_base_Path = getClass().getClassLoader().getResource("").getPath(); // String selfcertpath = demo_base_Path + Constant.SELFCERTPATH; // String trustcapath = demo_base_Path + Constant.TRUSTCAPATH; // String selfcertpath = demo_base_Path + Constant.SELFCERTPATH; // String trustcapath = demo_base_Path + Constant.TRUSTCAPATH; KeyStore selfCert = KeyStore.getInstance("pkcs12"); selfCert.load(getClass().getClassLoader().getResourceAsStream(NbConstant.CERT_FLODER + File.separatorChar + NbConstant.SELFCERTPATH), NbConstant.SELFCERTPWD.toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509"); kmf.init(selfCert, NbConstant.SELFCERTPWD.toCharArray()); // 2 Import the CA certificate of the server, KeyStore caCert = KeyStore.getInstance("jks"); caCert.load(getClass().getClassLoader().getResourceAsStream(NbConstant.CERT_FLODER + File.separatorChar + NbConstant.TRUSTCAPATH), NbConstant.TRUSTCAPWD.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509"); tmf.init(caCert); SSLContext sc = SSLContext.getInstance("TLS"); sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); // 3 Set the domain name to not verify // (Non-commercial IoT platform, no use domain name access generally.) SSLSocketFactory ssf = new SSLSocketFactory(sc, new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"}, null, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); // If the platform has already applied for a domain name which matches // the domain name in the certificate information, the certificate // domain name check can be enabled (open by default) // SSLSocketFactory ssf = new SSLSocketFactory(sc); // ClientConnectionManager ccm = this.getConnectionManager(); // SchemeRegistry sr = ccm.getSchemeRegistry(); // sr.register(new Scheme("https", 8743, ssf)); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", ssf) .build(); // -- allows multi-threaded use PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry); connMgr.setMaxTotal(200); connMgr.setDefaultMaxPerRoute(100); b.setConnectionManager(connMgr); // finally, build the HttpClient; // -- done! // httpClient = new DefaultHttpClient(ccm); httpClient = b.build(); }
创建一个 HttpClient 实例,这个实例需要调用 Dispose 方法释放资源,这里使用了 using 语句。接着调用 GetAsync,给它传递要调用的方法的地址,向服务器发送 Get 请求。