使用httpclient工具绕过证书验证进行HTTPS请求
在项目中需要调用其他服务的接口,该服务需要使用https访问,对此记录了一下,使用httpclient工具绕过证书验证进行HTTPS请求。
- 版本1:
import com.alibaba.fastjson.JSON; import org.apache.http.HttpEntity; 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.client.methods.HttpRequestBase; 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.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Map; public class HttpClientTest { private static final Logger logger = LoggerFactory.getLogger(HttpClientTest1.class); /** * httpclient绕过证书验证进行HTTPS请求 * url以https开头,会采用 https单向认证请求 * yrl以http开头,会使用httpPostRequest * * @param url * @param params json string * @param headers http header * @return */ public static String httpsClientSSLFuntcion(String url, Map<String, Object> headers, String params) throws Exception { if (url == null || !url.startsWith("http")) { logger.debug("httpClient发送失败,url异常!"); return null; } //响应内容 String responseContent = ""; if (url.startsWith("https")) { CloseableHttpClient httpclient = getHttpClient_ssl(); if (httpclient != null) { CloseableHttpResponse response = null; try { HttpPost httpPost = new HttpPost(url); //设置超时,连接超时,获取数据超时 RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(10000) .setSocketTimeout(30000).build(); httpPost.setConfig(requestConfig); if (headers != null) { for (Map.Entry<String, Object> param : headers.entrySet()) { httpPost.addHeader(param.getKey(), param.getValue().toString()); } } StringEntity se = new StringEntity(params, "UTF-8"); se.setContentType("application/json"); httpPost.setEntity(se); response = httpclient.execute(httpPost); //执行POST请求 HttpEntity entity = response.getEntity(); //获取响应实体 if (null != entity) { responseContent = EntityUtils.toString(entity, "UTF-8"); EntityUtils.consume(entity); //Consume response content } response.close(); } catch (Exception e) { logger.debug("httpclient发送异常:" + e.getMessage()); } finally { try { httpclient.close(); } catch (Exception e) { logger.debug("关闭httpclient连接异常:" + e.getMessage()); }//关闭连接,释放资源 } } } else { responseContent = httpPostRequest(url, headers, JSON.toJSONString(params)); } return responseContent; } private static CloseableHttpClient getHttpClient_ssl() { X509TrustManager xtm = new X509TrustManager() { //创建TrustManager public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; SSLContext ctx = null; try { ctx = SSLContext.getInstance("TLS"); //使用TrustManager来初始化该上下文,TrustManager只是被SSL的Socket所使用 ctx.init(null, new TrustManager[]{xtm}, null); } catch (Exception e) { logger.debug("httpclient异常"); } //创建SSLSocketFactory new String[] { "TLSv1" }, new String[]{"TLSv1.2"}, SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( ctx, new String[]{"TLS"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier() ); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder. <ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", socketFactory) .build(); // 创建ConnectionManager,添加Connection配置信息 PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager) .build(); return closeableHttpClient; } /** * 发送正常的post请求 * * @param url * @param headers Map<String, Object> 请求头 * @param params String 参数 json字符串 * @return String * @throws UnsupportedEncodingException */ public static String httpPostRequest(String url, Map<String, Object> headers, String params) throws Exception { HttpPost httpPost = new HttpPost(url); try { RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(1000).setConnectionRequestTimeout(1000) .setSocketTimeout(2000).build(); httpPost.setConfig(requestConfig); } catch (Exception e) { return null; } for (Map.Entry<String, Object> param : headers.entrySet()) { httpPost.addHeader(param.getKey(), param.getValue().toString()); } StringEntity se = new StringEntity(params); se.setContentType("application/json"); httpPost.setEntity(se); return getResult(httpPost); } private static String getResult(HttpRequestBase request) { URI uri = request.getURI(); String urlStr = uri.toString(); if (urlStr == null || "".equals(urlStr) || !urlStr.startsWith("http")) { return ""; } //不用连接池 CloseableHttpClient httpClient = HttpClients.custom().build(); try { CloseableHttpResponse response = httpClient.execute(request); try { HttpEntity entity = response.getEntity(); if (entity != null) { String result = EntityUtils.toString(entity, "UTF-8"); return result; } } finally { response.close(); } } catch (Exception e) { logger.error(e.getMessage()); } finally { try { httpClient.close(); } catch (IOException e) { logger.error(e.getMessage()); } } return ""; } }
在使用中调用httpsClientSSLFuntcion方法,但使用中发现报出异常:
java.lang.IllegalArgumentException: TLS at sun.security.ssl.ProtocolVersion.valueOf(ProtocolVersion.java:187) at sun.security.ssl.ProtocolList.convert(ProtocolList.java:84) at sun.security.ssl.ProtocolList.<init>(ProtocolList.java:52) at sun.security.ssl.SSLSocketImpl.setEnabledProtocols(SSLSocketImpl.java:2525) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:371) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:381) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) at com.dzh.httpclient.HttpClientTest1.httpsClientSSLFuntcion(HttpClientTest1.java:74) at com.dzh.httpclient.HttpClientTest1.main(HttpClientTest1.java:218)
发现是TLS版本问题:对方服务器限制只能使用TLSv1.2,但是程序中制定TLS(jdk8以后默认使用TLSv1.2)
修改后:
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpEntity;
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.client.methods.HttpRequestBase;
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.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
public class HttpClientTest {
private static final Logger logger = LoggerFactory.getLogger(HttpClientTest.class);
/**
* http post ,参数中有头信息 分发使用
*
* @param url
* @param headers Map<String, Object> 请求头
* @param params String 参数 json字符串
* @return String
* @throws UnsupportedEncodingException
*/
public static String httpPostRequest(String url, Map<String, Object> headers,
String params) throws Exception {
HttpPost httpPost = new HttpPost(url);
try {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(1000).setConnectionRequestTimeout(1000)
.setSocketTimeout(2000).build();
httpPost.setConfig(requestConfig);
} catch (Exception e) {
logger.debug("httpclient发送异常:" + e.getMessage());
return "0x001";
}
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpPost.addHeader(param.getKey(), param.getValue().toString());
}
StringEntity se = new StringEntity(params);
se.setContentType("application/json");
httpPost.setEntity(se);
return getResult(httpPost);
}
/**
* url以https开头,会采用 https单向认证请求
* yrl以http开头,会使用httpPostRequest
*
* @param url
* @param params Map<String, Object>
* @return
*/
public static String httpsClientSSLFuntcion(String url, Map<String, Object> headers, String params) throws Exception {
if (url == null || !url.startsWith("http")) {
logger.debug("httpClient发送失败,url异常!");
return "{\"success\":\"false\",\"message\":\"httpClient发送失败,url异常!\"}";
}
String responseContent = ""; //响应内容
if (url.startsWith("https")) {
CloseableHttpClient httpclient = getHttpClient_ssl();
if (httpclient != null) {
CloseableHttpResponse response = null;
try {
HttpPost httpPost = new HttpPost(url); //创建HttpPost
//设置超时,连接超时,获取数据超时
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setSocketTimeout(30000).build();
httpPost.setConfig(requestConfig);
if (headers != null) {
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpPost.addHeader(param.getKey(), param.getValue().toString());
}
}
StringEntity se = new StringEntity(params, "UTF-8");
se.setContentType("application/json");
httpPost.setEntity(se);
response = httpclient.execute(httpPost); //执行POST请求
HttpEntity entity = response.getEntity(); //获取响应实体
if (null != entity) {
responseContent = EntityUtils.toString(entity, "UTF-8");
EntityUtils.consume(entity); //Consume response content
}
response.close();
} catch (Exception e) {
logger.debug("httpclient发送异常:" + e.getMessage());
} finally {
try {
httpclient.close();
} catch (IOException e) {
logger.debug("关闭httpclient连接异常:" + e.getMessage());
}//关闭连接,释放资源
}
}
} else {
responseContent = httpPostRequest(url, headers, JSON.toJSONString(params));
}
return responseContent;
}
/**
* 在调用SSL之前需要重写验证方法,取消检测SSL
* 创建ConnectionManager,添加Connection配置信息
*
* @return HttpClient 支持https
*/
private static CloseableHttpClient getHttpClient_ssl() {
X509TrustManager xtm = new X509TrustManager() { //创建TrustManager
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
//TLS1.0与SSL3.0基本上没有太大的差别,可粗略理解为TLS是SSL的继承者,但它们使用的是相同的SSLContext
SSLContext ctx = null;
try {
ctx = SSLContext.getInstance("TLS");
//使用TrustManager来初始化该上下文,TrustManager只是被SSL的Socket所使用
ctx.init(null, new TrustManager[]{xtm}, null);
} catch (Exception e) {
e.printStackTrace();
}
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.
<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory)
.build();
// 创建ConnectionManager,添加Connection配置信息
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager)
.build();
return closeableHttpClient;
}
private static String getResult(HttpRequestBase request) {
URI uri = request.getURI();
String urlStr = uri.toString();
if (urlStr == null || "".equals(urlStr) || !urlStr.startsWith("http")) {
return "0x001";
}
//不用连接池
CloseableHttpClient httpClient = HttpClients.custom().build();
try {
CloseableHttpResponse response = httpClient.execute(request);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, "UTF-8");
return result;
}
} finally {
response.close();
}
} catch (Exception e) {
logger.error(e.getMessage());
} finally {
try {
httpClient.close();
} catch (IOException e) {
logger.error(e.getMessage());
}
}
return "0x001";
}
}