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文件。
* 如何生成文件,请查看
*/
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();
}




时刻与技术进步,每天一点滴,日久一大步!!! 本博客只为记录,用于学习,如有冒犯,请私信于我。