连接管理
HttpClient 有一个对连接初始化和终止,还有在活动连接上 I/O 操作的完整控制。而连接操作的很多方面可以使用一些参数来控制。
一、套接字工厂
HttpClientContext clientContext = HttpClientContext.create(); /** * 连接套接字 依靠 ConnectionSocketFactory接口去创建、初始化、连接。 * ConnectionSocketFactory * PlainConnectionSocketFactory 是创建和初始化原生(非加密的)套接字的默认工厂类。 * LayeredConnectionSocketFactory 也是一个接口, 是 ConnectionSocketFactory接口的扩展 * SSLConnectionSocketFactory SSL/TLS层的连接套接字, 可以建立SSL连接。 * 注:HttpClient不适用任何自定义的加密功能,它完全依赖于标准的Java Cryptography(JCE) 和 安全套接字扩展(JSEE)。 */ PlainConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory(); // 创建并初始化套接字 Socket socket = sf.createSocket(clientContext); int timeout = 1000;// ms HttpHost target = new HttpHost("localhost"); InetSocketAddress remoteAddress = new InetSocketAddress("127.0.0.1", 8888); // 连接套接字 sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext); //套接字连接成功后,就可以使用Socket读写数据了, 一般不仅仅是读写数据,如果这样,比纯粹使用Socket网络编程还麻烦 // 获取输出流:OutputStream outputStream = socket.getOutputStream(); // 获取输入流:InputStream inputStream = socket.getInputStream();
二、SSL/TLS 的定制
SSL证书,也称为服务器SSL证书,是遵守SSL协议的一种数字证书由全球信任的证书颁发机构(CA)验证服务器身份后颁发将SSL证书安装在网站服务器上,可实现网站身份验证和数据加密传输双重功能。
/** * 使用带证书的定制 SSL访问 * 程序中使用了my.store这个文件,这个文件不是网站的证书,而是一份包含自己密码的自己的证书库。 * 这个文件是需要自己生成的,使用jdk中的keytool命令可以很方便的生成my.store文件。 * 如何生成文件,请查看:https://www.cnblogs.com/myitnews/p/12206094.html */ public void createHttpSSL() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, IOException, CertificateException { //是否绕过SSL boolean isSSL = false; //证书 File creFile = new File("C:\\Users\\Administrator\\Desktop\\my.store"); //证书库密码, 生成my.store时输入的口令 String crePwd = "mypassword"; /** * 1. 创建 SSL上下文对象 */ SSLContext sslContext = null; if (!isSSL) { sslContext = SSLContext.getInstance("SSLv3"); // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法 X509TrustManager x509TrustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }; sslContext.init(null, new TrustManager[] {x509TrustManager}, null); } else { if (null != creFile && creFile.length() > 0) { if (null != crePwd) { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(new FileInputStream(creFile), crePwd.toCharArray()); sslContext = SSLContexts.custom().loadTrustMaterial(keyStore, new TrustSelfSignedStrategy()).build(); } else { throw new SSLHandshakeException("密码为空"); } } } /** * 2. 注册 */ Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.getDefaultHostnameVerifier())) .build(); /** * 3. SSL注册到连接管理器中 */ PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry); connManager.setMaxTotal(1000); // 连接池最大连接数 connManager.setDefaultMaxPerRoute(20); // 每个路由最大连接数 /** * 4. 发送请求 */ CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connManager).build(); //12306:https://kyfw.12306.cn/otn/login/init //支付宝:https://www.alipay.com/, 这里如果使用HttpPost会报403 HttpGet httpPost = new HttpGet("https://www.alipay.com/"); CloseableHttpResponse response = httpClient.execute(httpPost); System.out.println("响应行:" + response.getStatusLine()); System.out.println("响应内容:"+EntityUtils.toString(response.getEntity(), Consts.UTF_8)); }
三、主机名验证
/** * 主机名验证 * 除了在 SSL/TSL协议层扮演信任验证和客户端认证角色之外,HttpClient能选择性的去验证 * 目标主机名称是否和储存在服务器中X.509证书上的名称一致。一旦连接已经建立,验证过程能提供服务器信任材料的额外可靠性保证。 * javax.net.ssl.HostnameVerifier接口体现了一种主机名验证策略。 * javax.net.ssl.HostnameVerifier有两种实现HttpClient可以用来工作: * DefaultHostnameVerifier:默认实现,遵从RFC2818。主机名必须符合指定证书上任意备选名称。 * NoopHostnameVerifier:该主机名验证器本质上会关闭主机名验证。它接受任何有效的和符合目标主机的SSL会话。 * * 注意:主机名验证和 SSL信任验证不可混淆。 */ public void hostVerifier() throws IOException { //每一个HttpClient会使用默认的DefaultHostnameVerifier实现,如果你想也可以指定实现 //SSLContext sslContext = SSLContexts.createSystemDefault(); //SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); //HttpClients.custom().setSSLSocketFactory(sslsf); //HttpClient 4.4版本使用公共后缀列表来保证SSL证书中的通配符不会被误用,当应用在有一个共同顶级域名下的子域名的时候。 //参考:https://publicsuffix.org/list/effective_tld_names.dat】。强烈建议制作一个列表的本地副本。 PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load( PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat")); DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher); // 禁用公共后缀验证 // DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null); HttpClientBuilder clientBuilder = HttpClients.custom(); //绑定主机验证 clientBuilder.setSSLHostnameVerifier(hostnameVerifier); CloseableHttpClient httpClient = clientBuilder.build(); }
时刻与技术进步,每天一点滴,日久一大步!!!
本博客只为记录,用于学习,如有冒犯,请私信于我。