httpclient4.x访问https[通俗易懂]

2022-09-15 15:21:53 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

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

0 人点赞