https双向认证P12客户端证书,使用HttpClient来发送带客户端证书请求

由于业务需求需要外呼调用三方接口,在客户端向服务器端发送带证书的请求这里有一点问题,网上的例子大多都不太好使,经过查询资料总结尝试,大概明白了。

写成工具类分享给遇到同样问题的小伙伴,亲测可用

import com.epoint.core.utils.string.StringUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;

public class HttpsRequest {
    //.p12文件路径
    private String filePath;
    //密码
    private String passWord;
    //外呼url
    private String url;
    // 请求体
    private String body;
    //请求头
    private Map<String, String> header;
    //代理IP
    private String proxyIP;
    //代理端口
    private int proxyPort;


    private HttpsRequest(Builder builder) {
        this.filePath = builder.filePath;
        this.passWord = builder.passWord;
        this.url = builder.url;
        this.body = builder.body;
        this.header = builder.header;
        this.proxyIP = builder.proxyIP;
        this.proxyPort = builder.proxyPort;
    }

    public static class Builder {
        //.p12文件路径
        private String filePath;
        //密码
        private String passWord;
        //外呼url
        private String url;
        // 请求体
        private String body;
        //请求头
        private Map<String, String> header = new HashMap<>();
        //代理IP
        private String proxyIP;
        //代理端口
        private int proxyPort;


        public Builder filePath(String filePath) {
            this.filePath = filePath;
            return this;
        }

        public Builder passWord(String passWord) {
            this.passWord = passWord;
            return this;
        }

        public Builder url(String url) {
            this.url = url;
            return this;
        }

        public Builder body(String body) {
            this.body = body;
            return this;
        }

        public Builder header(String key, String value) {
            this.header.put(key, value);
            return this;
        }


        public Builder proxy(String ip, int port) {
            this.proxyPort = port;
            this.proxyIP = ip;
            return this;
        }

        public HttpsRequest build() {
            return new HttpsRequest(this);
        }
    }


    public String doPost() {
        String rep = "";
        SSLContext sslcontext;
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            try (FileInputStream fileInputStream = new FileInputStream(filePath)) {
                keyStore.load(fileInputStream, passWord.toCharArray());

                sslcontext = SSLContexts.custom()
                        //忽略掉对服务器端证书的校验
                        .loadTrustMaterial((TrustStrategy) (chain, authType) -> true)
                        //加载服务端提供的truststore(如果服务器提供truststore的话就不用忽略对服务器端证书的校验了)
                        //.loadTrustMaterial(new File("D:\\truststore.jks"), "123456".toCharArray(),
                        //        new TrustSelfSignedStrategy())
                        .loadKeyMaterial(keyStore, passWord.toCharArray())
                        .build();
            }
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[]{"TLSv1"},
                    null,
                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());


            try (CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build()) {
                HttpPost httpPost = new HttpPost(url);
                StringEntity req = new StringEntity(body, "UTF-8");
                httpPost.setEntity(req);
                if (header != null) {
                    header.entrySet().stream().forEach((h) -> {
                        httpPost.addHeader(h.getKey(), h.getValue());
                    });
                }
                RequestConfig config;
                if (!StringUtil.isBlank(proxyIP) && !StringUtil.isBlank(proxyPort)) {
                    HttpHost proxy = new HttpHost(proxyIP, proxyPort);
                    config = RequestConfig.custom().setProxy(proxy).setConnectionRequestTimeout(5000)
                            .setSocketTimeout(30000).setConnectTimeout(20000).build();
                } else {
                    config = RequestConfig.custom().setConnectionRequestTimeout(5000)
                            .setSocketTimeout(30000).setConnectTimeout(20000).build();
                }
                //连接超时时间, 单位毫秒
                //requestConfigBuilder.setConnectTimeout(2000);
                //从池中获取连接超时时间
                //requestConfigBuilder.setConnectionRequestTimeout(500);
                //读超时时间(等待数据超时时间)
                //requestConfigBuilder.setSocketTimeout(2000);

                httpPost.setConfig(config);


                try (CloseableHttpResponse httpResponse = client.execute(httpPost)) {
                    HttpEntity entity = httpResponse.getEntity();
                    rep = EntityUtils.toString(entity);
                }
            }
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        }
        return rep;
    }

}

 

  调用样例

 String result = new HttpsRequest.Builder()
                .body(gov_req_str)
                .filePath(file)
                .passWord(password)
                .url(url)
                .proxy(proxyIp, Integer.parseInt(proxyPort))
                .header("charset", "UTF-8")//头信息,多个头信息多次调用此方法即可
                .header("Content-Type", "application/json")
                .build()
                .doPost();

     建造者模式使代码更简洁,使得该类不可变,一致,同时保证了多线程情况下的线程安全。详见effective java第二章第二条。

    为什么不使用lombok的@Builder: 目的是想要实现多个头信息多次调用.header,直接传map的话体验不是很好

    https原理 https://www.jianshu.com/p/29a90d057510

  

posted @ 2021-02-19 19:08  威猛先森  阅读(695)  评论(0)    收藏  举报