这两天研究了下springboot 请求https,记录一下遇到的问题及解决方案。
添加依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
对于需要添加证书的请求:
添加证书
(一)证书的下载
先通过浏览器将未签名验证的证书保存到本地, 点击 不安全–> 证书–> 详细信息 --> 复制到文件 然后默认选择 起一个文件名 , 保存即可, 比如我将证书保存在了桌面 , 命名为 file.cer(名称自定义,cer格式)
(二)证书导入JDK
如何把上面那步的(file.cer)这个证书导入java中的cacerts证书库里?
如:我的 jdk安装这个目录在 C:\Program Files\Java\jdk1.8.0_191
开始 >> 运行 >> 输入cmd 进入dos命令行 >>
再用cd进入到C:\Program Files\Java\jdk1.8.0_191\jre\lib\security这个目录下
敲入如下命令回车执行
keytool -import -alias cacerts -keystore cacerts -file C:\Users\huihe10\Desktop\file.cer
你敲入changeit就行了,这java中cacerts证书库的默认密码
若是changeit不行就写 changeme 一般的 chageit 就可以了。
如果提示:“ 是否信任此证书?[ 否]:” ,那么请输入“y”
当出现:“证书已添加到密钥库中[ 正在存储cacerts] ”的时候,那么恭喜你已经添加成功。
tip:这个步骤也可参考连接: https://blog.csdn.net/liruiqing/article/details/80416740
(三)生成keystore文件
只将证书导入JDK就可以了吗? 我这里验证的是不可以的, 必须还要生成对应的 keystore文件
keystore文件生成命令: keytool -import -file xx.cer -keystore xx.keystore
说明: 执行改命令时,进入证书所在的文件夹,我的是 C:\Users\huihe10\Desktop\file.cer
(若是提示权限不够 linux那就再加sudo , windows就以管理员的身份执行)
回车后又会让你输入密码 , 那么就还对应着输入 chageit 即可
执行完毕后会在当前路径下再产生一个xx.keystore文件.
xx:自定义,我这里叫file
项目中配置
- 将上面上传的xx.keystore 文件文件复制到你的项目的类路径下(srv/main/resource下面)
- 将下面的这个restTemplate的配置复制到你的项目中去,其中里面用到了一个httpConverter 这个是做json格式转换的, 和HTTPS没太大关系 , 若是不需要就将它以及相关代码删掉即可。
@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);
SSLContextBuilder builder = new SSLContextBuilder();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
ClassPathResource resource = new ClassPathResource("nonghang.keystore");
InputStream inputStream = resource.getInputStream();
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 = restTemplate.getMessageConverters();
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;
}
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration({"/static/applicationContext.xml"})
public class DemoApplicationTests {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoApplicationTests.class);
@Autowired
private RestTemplate restTemplate;
@Test
public void doHttps(){
try {
String url = "https://xxx.boshi.com.cn";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity requestEntity = new HttpEntity<>(headers);
String responseBody = restTemplate.getForObject(url, String.class);
System.out.println(responseBody);
}catch (Exception e){
e.printStackTrace();
}
}
}
Tips
对于不需要添加证书的请求,可以用以下代码:
@Bean
public RestTemplate restTemplate() {
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
return new RestTemplate(requestFactory);
}
private HttpClient httpClient(){
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(100);
connectionManager.setValidateAfterInactivity(2000);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(65000) // 服务器返回数据(response)的时间,超时抛出read timeout
.setConnectTimeout(5000) // 连接上服务器(握手成功)的时间,超时抛出connect timeout
.setConnectionRequestTimeout(1000)// 从连接池中获取连接的超时时间,超时抛出ConnectionPoolTimeoutException
.build();
return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
}
如果要绕开证书信任,可以用以下代码:
@Bean
public RestTemplate buildRestTemplate(*//*List<CustomHttpRequestInterceptor> interceptors*//*) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectionRequestTimeout(6500);
factory.setConnectTimeout(6500);
factory.setReadTimeout(5000);
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, (X509Certificate[] x509Certificates, String s) -> true);
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, 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<ClientHttpRequestInterceptor> clientInterceptorList = new ArrayList<>();
for (CustomHttpRequestInterceptor i : interceptors) {
ClientHttpRequestInterceptor interceptor = i;
clientInterceptorList.add(interceptor);
}
restTemplate.setInterceptors(clientInterceptorList);*//*
return restTemplate;