使⽤RestTemplate调⽤https接⼝跳过证书验证
⽬录
RestTemplate调⽤https接⼝跳过证书验证
RestTemplate访问https遇到SSL证书验证错误
添加Https证书
下载证书
导⼊证书
⽣成keystore⽂件
项⽬中配置
RestTemplate调⽤https接⼝跳过证书验证
import javax.ssl.HostnameVerifier;
import javax.ssl.SSLContext;
import javax.ssl.SSLSession;
import javax.ssl.TrustManager;
import javax.ssl.X509TrustManager;
import java.nio.charset.StandardCharsets;
import CertificateException;
import org.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.verter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
public class NoHttpsClientUtils {
/**
* 跳过证书效验的sslcontext
*
* @return
* @throws Exception
*/
private static SSLContext createIgnoreVerifySSL() throws Exception {
SSLContext sc = Instance("TLS");
// 实现⼀个X509TrustManager接⼝,⽤于绕过验证,不⽤修改⾥⾯的⽅法
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sc.init(null, new TrustManager[] { trustManager }, null);
return sc;
}
/**
* 构造RestTemplate
*
* @return
* @throws Exception免费ssl证书永久生成
*/
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() {
@Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
/
/ 解决中⽂乱码问题
return restTemplate;
}
}
RestTemplate访问https遇到SSL证书验证错误
今天,遇到了⼀个SSL证书认证的问题,纠结了好久才解决,学到了很多东西,记录下来,分享给⼤家。
⾸先说⼀下⼤概的背景,我们的项⽬对接了第三⽅接⼝,需要向对⽅发送https请求。那么问题来了,https请求时需要通过SSL证书认证的,今天流程突然⾛不下去了,看
⽇志发现是SSL认证的问题:
在⽹上资料,解决问题有两个思路:
1.通过相关配置,跳过SSL证书验证,完成请求。
2.下载证书,将证书导⼊到JDK的证书管理⾥⾯。
第⼀种⽅法相当于是取巧,逃避问题,我们当然不能那么做了,我们要迎接挑战,所以⼩编直接pass第⼀条,选择第⼆条。
添加Https证书
下载证书
先通过浏览器将未签名验证的证书保存到本地, 具体步骤:
点击不安全–> 证书–> 详细信息 --> 复制到⽂件然后默认选择起⼀个⽂件名 , 保存即可。
(Mac下载:Mac不到复制的按钮。。。只能另辟蹊径了)点地址栏上的⼩锁,然后点证书,然后将蓝⾊的⽂件拖到要下载的⽂件夹即可。最好给它重命名⽅便使⽤。导⼊证书
我们需要将证书导⼊到JDK的证书管理⾥⾯才能咋项⽬中使⽤我们刚刚下载的证书。导⼊命令如下:
keytool -import -noprompt -trustcacerts -alias  -keystore
/Users/ZHONGHUI/develop/jdk-12.0.2.jdk/Contents/Home/lib/security/cacerts
-storepass changeit -
其中,因为我们⽤的是JDK12,跟常⽤的JDK8⽬录是不⼀样的,所以我的cacerts⽬录是:/Users/ZHONGHUI/develop/jdk-
12.0.2.jdk/Contents/Home/lib/security/cacerts,如果你⽤的是JDK1.8或者1.7的话,那么你的cacerts在jre下的 /lib/security/下。eleme是我们给复制过来的证书取得别名,就是我们下载改名后的证书。changeit 是密码,如果changeit不⾏的话,就试试changeme,⼀般是changeit。
⽣成keystore⽂件
keytool -import -storepass changeit - -keystore eleme.keystore
然后就会⽣成⼀个eleme.keystore⽂件,若权限不够,加sudo即可。将它复制到项⽬的类路径下。
项⽬中配置
将下⾯的RestTemplateConfig替换原来的:
onfig;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.fig.Registry;
import org.fig.RegistryBuilder;
import org.socket.ConnectionSocketFactory;
import org.socket.PlainConnectionSocketFactory;
import org.ssl.NoopHostnameVerifier;
import org.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import t.annotation.Bean;
import t.annotation.Configuration;
import io.ClassPathResource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.verter.HttpMessageConverter;
import org.verter.json.MappingJackson2HttpMessageConverter;
import org.l.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
@Configuration
public class RestTemplateConfig {
@Autowired
private FastJsonHttpMessageConverter httpMessageConverter;
@Bean
RestTemplate restTemplate() throws Exception {
HttpComponentsClientHttpRequestFactory factory = new
HttpComponentsClientHttpRequestFactory();
factory.setConnectionRequestTimeout(5 * 60 * 1000);
factory.setConnectTimeout(5 * 60 * 1000);
factory.setReadTimeout(5 * 60 * 1000);
// https
SSLContextBuilder builder = new SSLContextBuilder();
KeyStore keyStore = DefaultType());
ClassPathResource resource = new ClassPathResource("nonghang.keystore");
InputStream inputStream = InputStream();
keyStore.load(inputStream, null);
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", socketFactory).build();
PoolingHttpClientConnectionManager phccm = new PoolingHttpClientConnectionManager(registry);
phccm.setMaxTotal(200);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).setConnectionManager(phccm).setConnectionManagerShared(true).build();        factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
List<HttpMessageConverter<?>> converters = MessageConverters();
ArrayList<HttpMessageConverter<?>> convertersValid = new ArrayList<>();
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter ||
converter instanceof MappingJackson2XmlHttpMessageConverter) {
continue;
}
convertersValid.add(converter);
}
convertersValid.add(httpMessageConverter);
restTemplate.setMessageConverters(convertersValid);
inputStream.close();
return restTemplate;
}
}
好啦,万事俱备只⽋东风,⾃信满满的试了⼀下,发现还是⾏不通,深受打击的我决定还是去看看导致这类报错的原因。
从根源出发,https的socket⼯作原理是怎样的?
https的socket⼯作原理流程图如下:
那么,什么是SSL证书?
SSL 证书就是遵守 SSL协议,由受信任的数字证书颁发机构CA,在验证服务器⾝份后颁发,具有服务器⾝份验证和数据传输加密功能。
⾸先,要知道导致报这个异常的原因不仅仅是因为证书校验不通过。
1.在我们通过https链接服务器时,服务器会给我们返回⼀个证书,这个证书可能经过CA认证,也可能是未认证的⾃制证书,客户端拿到这个证书后会对这个证
书进⾏验证,如果是经过CA验证的证书,⾃然证书校验就能通过,⾃制证书⾃然就校验不同过,从⽽导致上边的异常。
2.证书校验通过后,还需要校验访问的域名是否和证书指定的域名是否匹配。未匹配也会导致如上异常。
3.上边两步都校验通过了才开始进⾏握⼿,但握⼿也有可能失败,从⽽导致上边的异常。
以上三个步骤中任何⼀个出了问题,都会连接失败。
⾸先,我们获取的证书是经过CA认证的,理应不会出现不通过的情况,第三⽅的域名也是没有问题的。那么到底是哪⾥出了问题呢?
后来我们从⽹络组那边了解到,他们那边对最近做了⼤的改动,之前的proxy都要重新加,加的时候把我们的给漏掉了。。。。⾟苦⼤半天,被坑惨了啊。。
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。