需求
最近在负责一个对接第三方服务的事情,在对接期间,因为第三方服务为https的请求,众所周知,请求https请求会使用本地的证书公钥去访问服务SSL证书,应我本地并没有对应的SSL证书,所以请求不到服务,请求接口时报如下错误。翻阅资源发现是可以 忽略SSL认证的。
- unable to find valid certification path to requested target
工具类
import org.apache.http.client.HttpClient;import org.apache.http.conn.ssl.NoopHostnameVerifier;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import org.springframework.http.converter.StringHttpMessageConverter;import org.springframework.web.client.RestTemplate;import java.nio.charset.StandardCharsets;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSession;import javax.net.ssl.TrustManager;import javax.net.ssl.X509TrustManager;public class SslUtils {private static void trustAllHttpsCertificates() throws Exception {TrustManager[] trustAllCerts = new TrustManager[1];TrustManager tm = new miTM();trustAllCerts[0] = tm;SSLContext sc = SSLContext.getInstance("SSL");sc.init(null, trustAllCerts, null);HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());}static class miTM implements TrustManager, X509TrustManager {@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}public boolean isServerTrusted(X509Certificate[] certs) {return true;}public boolean isClientTrusted(X509Certificate[] certs) {return true;}@Overridepublic void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {return;}@Overridepublic void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {return;}}/** * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用 * * @throws Exception */public static void ignoreSsl() throws Exception {HostnameVerifier hv = new HostnameVerifier() {@Overridepublic boolean verify(String urlHostName, SSLSession session) {System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());return true;}};trustAllHttpsCertificates();HttpsURLConnection.setDefaultHostnameVerifier(hv);}/** * 避免HttpClient的”SSLPeerUnverifiedException: peer not authenticated”异常 * * 不用导入SSL证书 * * @param base * @return */
public static HttpClient wrapClient(HttpClient base) {try {SSLContext ctx = SSLContext.getInstance("TLS");X509TrustManager tm = new X509TrustManager() {@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}@Overridepublic void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}};ctx.init(null, new TrustManager[]{tm}, null);SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(ssf).build();return httpclient;} catch (Exception ex) {ex.printStackTrace();return HttpClients.createDefault();}}/** * 跳过证书效验的sslcontext * * @return * @throws Exception */private static SSLContext createIgnoreVerifySSL() throws Exception {SSLContext sc = SSLContext.getInstance("TLS");// 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法X509TrustManager trustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {}@Overridepublic void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {}@Overridepublic java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;}};sc.init(null, new TrustManager[] { trustManager }, null);return sc;}/** * 构造RestTemplate * * @return * @throws Exception */public static RestTemplate getRestTemplate() throws Exception {HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();// 超时//factory.setConnectionRequestTimeout(5000);//factory.setConnectTimeout(5000);//factory.setReadTimeout(5000);SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(createIgnoreVerifySSL(),// 指定TLS版本null,// 指定算法null,// 取消域名验证new HostnameVerifier() {@Overridepublic boolean verify(String string, SSLSession ssls) {return true;}});CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();factory.setHttpClient(httpClient);RestTemplate restTemplate = new RestTemplate(factory);// 解决中文乱码问题restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));return restTemplate;}}
使用方法
@Overridepublic void completionIndexPushToODS(List<Bean> data) throws Exception {try {//请求地址String url ="https://.....";//请求参数String requestData = JSON.toJSONString(data);System.out.println("请求参数:" + requestData);RestTemplate restTemplate;if (flag) {//HTTP绕过SSL证书认证restTemplate = SslUtils.getRestTemplate();} else {//普通请求方式restTemplate = new RestTemplate();}//自定义header传输信息(根据自己的需求设置)HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity(JSON.toJSON(data), httpHeaders);URI uri = new URI(url);System.out.println("开始请求");//POST返回字节方式byte[] response = restTemplate.postForObject(uri, httpEntity, byte[].class); //GET请求返回字符//String request = restTemplate.getForObject(uri, String.class);System.out.println("请求结束");if (response == null) {System.out.println("返回值为空");}String result = new String(response, "utf-8");System.out.println("返回结果:" + result);} catch (Exception e) {e.printStackTrace();}}