大家好,又见面了,我是你们的朋友全栈君。
https有单向认证和双向认证之分,单向认证即客户端只会认证服务端,双向认证是客户端需要认证服务端,服务端也需要认证客户端。
先说单向认证,浏览器访问服务端,服务端接收请求,会把证书(包含密钥和其他信息)和加密后响应返回给浏览器。如果这个证书不是向第三方权威机构申请的,浏览器会提示证书有问题(使用httpclient访问的话会报错)。如果忽略错误,则浏览器接受证书并解密响应,发送的数据也用此密钥加密。
双向认证的话,客户端访问服务端也要提供证书,否则服务端拒绝响应。而且如果是自己生产的证书,需要把客户端的证书导入到服务端的信任列表中,否则服务端也会拒绝。
前面说到,如果服务端的证书不是向第三方权威机构申请,使用httpclient访问会报错。解决办法由两种,第一种是将证书导入jre的密钥库的信任列表;第二种是让他不去验证服务端证书。如果需要双向认证,还需要为httpclient指定客户端需要使用的证书。
代码语言:javascript复制package cloudolp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
*
*/
public class HttpClientFactory {
private static CloseableHttpClient httpClient;
public synchronized static CloseableHttpClient getCloseableHttpClient(){
if(httpClient==null){
SSLConnectionSocketFactory sslConnectionSocketFactory=null;
try{
FileInputStream keyin = new FileInputStream(new File("d:\client.p12"));
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(keyin, "client1".toCharArray());
keyin.close();
final TrustStrategy trustStrategy=new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(trustStrategy)
.loadKeyMaterial(keystore, "client1".toCharArray())
.build();
sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslcontext,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
} catch(Exception e){
LogFactory.getLog(HttpClientFactory.class).error(e);
}
Registry<ConnectionSocketFactory> registry= RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory)
.build();
PoolingHttpClientConnectionManager connectionManager=new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);
HttpClientBuilder httpClientBuilder=HttpClients.custom();
// if(sslConnectionSocketFactory!=null){
// httpClientBuilder.setSSLSocketFactory(sslConnectionSocketFactory);
// }
httpClientBuilder.setConnectionManager(connectionManager);
httpClient = httpClientBuilder.build();
}
return httpClient;
}
}
代码语言:javascript复制final TrustStrategy trustStrategy=new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
自己实现证书信任策略,这里我们之间返回true,这样所有的服务端证书都会被信任,这样如果服务端的证书不是权威机构颁布的,httclient就不会报错了
代码语言:javascript复制SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(trustStrategy)
.loadKeyMaterial(keystore, "client1".toCharArray())
.build();
loadTrustMaterial()是设置服务端证书的信任策略,这个方法有很多重载的方法,比如可以使用密钥库
代码语言:javascript复制loadKeyMaterial()设置客户端需要发送到服务端的证书,有两个参数,密钥库和密钥密码,密钥库是client1.p12,keystore.load()也有两个参数,一个是真
代码语言:javascript复制正的存储地址,一个是密钥库的秘密(有可能和密钥的秘密不一样)。这里的证书类型是PKCS12,是个人证书。
代码语言:javascript复制FileInputStream keyin = new FileInputStream(new File("d:\client.p12"));
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(keyin, "client1".toCharArray());
最后通过SSLContext 创建套接字连接工厂,并注册到连接管理器。
注意我是用的CloseableHttpClient和PoolingHttpClientConnectionManager
如果用的是DefaultHttpClient,可以用下面这段代码
HttpClient httpClient = new DefaultHttpClient(); SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext); Scheme sch = new Scheme(“https”, 8443, socketFactory); httpClient.getConnectionManager().getSchemeRegistry().register(sch);
但如果用的不是DefaultHttpClient,而是CloseableHttpClient,代码ttpClient.getConnectionManager().getSchemeRegistry().register(sch)会报错,在实际中应该很少会使用DefaultHttpClient。
如果没有设置连接管理器,则可在builder中设置套接字连接工厂
httpClientBuilder.setSSLSocketFactory(sslConnectionSocketFactory);
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/163258.html原文链接:https://javaforall.cn