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

浙公网安备 33010602011771号