本文出处:http://blog.csdn.net/chaijunkun/article/details/40145685,转载请注明。因为本人不定期会整理相关博文,会对相应内容作出完好。因此强烈建议在原始出处查看此文。
HttpClient在当今Java应用中的位置越来越重要。从该项目的变迁过程我们不难发现,其已经从apache-commons众多的子项目中剥离,一跃成为现在的顶级项目。可见它的分量。然而随着项目的升级和架构的调整。非常多曾经经常使用的类和方法都已被打上了@Deprecated注解,作为一个有代码洁癖的程序员。我们也有必要升级一下工具类,让代码更加整洁。
另外在项目中正好须要訪问https协议的接口。而相应的server没有购买商业CA颁发的正式受信证书,仅仅是做了个自签名(联想一下12306站点购票时提示的那个警告信息)。默认情况下通过HttpClient訪问会抛出异常,在本文中也给出了解决的方法。
在HttpClient 4.x版本号中引入了大量的构造器设计模式。非常多的配置都不建议直接new出来。并且相关的API也有所修改,比如连接參数,曾经是直接new出HttpConnectionParams对象后通过set方法逐一设置属性,现在有了构造器。能够通过例如以下方式进行构造:
ConnectionConfig.custom().setCharset(Charsets.toCharset(defaultEncoding)).build();
SocketConfig.custom().setSoTimeout(100000).build();
基本情况就是这样,接下来谈下怎样使用新的HttpClient来訪问自签名https接口。
參阅CSDN博主noodies代码,发现实现訪问自签名https的要点就是建立一个自己定义的SSLContext对象。该对象要有能够存储信任密钥的容器。还要有推断当前连接是否受信任的策略,以及在SSL连接工厂中取消对全部主机名的验证。他的代码将会在本文最后贴出来,下面代码均针对新HttpClient。
首先建立一个信任不论什么密钥的策略。代码非常easy,不去考虑证书链和授权类型,均觉得是受信任的:
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<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create(); ConnectionSocketFactory plainSF = new PlainConnectionSocketFactory(); registryBuilder.register("http", plainSF); //指定信任密钥存储对象和连接套接字工厂 try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, new AnyTrustStrategy()).build(); LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_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();
在上述代码中能够看到。首先建立了一个密钥存储容器,随后让SSLContext开启TLS,并将密钥存储容器和信任不论什么主机的策略载入到该上下文中。构造SSL连接工厂时,将自己定义的上下文和同意不论什么主机名通过校验的指令一并传入。最后将这样一个自己定义的SSL连接工厂注冊到https协议上。
前期准备已经完毕,接下来我们要获得HttpClient对象:
//设置连接管理器 PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry); connManager.setDefaultConnectionConfig(connConfig); connManager.setDefaultSocketConfig(socketConfig); //构建客户端 HttpClient client= HttpClientBuilder.create().setConnectionManager(connManager).build();
为了让我们的HttpClient具有多线程处理的能力,连接管理器选用了PoolingHttpClientConnectionManager,将协议注冊信息传入连接管理器,最后再次利用构造器的模式创建出我们须要的HttpClient。随后的GET/POST请求发起方法http和https之间没有差异。
为了验证我们的代码是否成功,能够做下JUnit单元測试:
@Test public void doTest() throws ClientProtocolException, 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代码
为了方便大家使用,本人将封装好的代码上传到了CSDN资源共享中,欢迎下载。
下载地址:http://download.csdn.net/detail/chaijunkun/8046331
最后感谢CSDN博主noodies的代码提供了思路:http://blog.csdn.net/noodies/article/details/17240805
本文中的部分代码參阅了HttpClient 4.3.5官方文档的2.7节关于Connection socket factories的内容