RestTemplate发送http,https默认信任所有链接以及https携带证书的请求工具。不依赖apache的httpcomponents
这里主要是处理https携带证书,其他的http和常规https不是很复杂
通常情况下接口提供方都会提供证书,基本都是 cer格式证书(也可能会有其他格式),需要自行转换java直接可以调用的证书 keystore格式,直接使用jdk提供工具:
keytool -importcert -keystore [自定义证书名称].keystore -file ./[需要转换的cer证书].cer -alias [别名] -storepass [设置密码] -noprompt 如:
keytool -importcert -keystore yxt.keystore -file ./xxxx.server.cer -alias ydy -storepass yxt_123456 -noprompt
需要注意的是jdk的版本不同使用的TLS版本也不一样,这里使用的是jdk1.8
加载证书发起https访问,主要怎么去配置https携带证书
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* 自主携带https的自定义证书进行访问通信
*
* @author Yang douya
* @date 2021/5/27 9:57
*/
@Slf4j
public class HttpsClientRequestFactory2SSLCert extends SimpleClientHttpRequestFactory {
/**
* 证书位置
*/
private String certPath;
/**
* 证书密码 未知直接为 null
*/
private String certPassword;
public HttpsClientRequestFactory2SSLCert(String certPath, String certPassword) {
this.certPassword = certPassword;
this.certPath = certPath;
}
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustManagers = {new SSLCert509TrustManager(certPath, certPassword)};
// 强制使用 TLSv1.2 解决 Received fatal alert: protocol_version; nested exception is javax.net.ssl.SSLException: Received fatal alert: protocol_version 异常情况
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, trustManagers, new SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* SSLSocketFactory用于创建 SSLSockets
*/
private class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
// 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
// 这些默认的服务的最低质量要求保密保护和服务器身份验证
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
// 返回的密码套件可用于SSL连接启用的名字
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"});
return socket;
}
}
/**
* 配置https携带证书
*/
private class SSLCert509TrustManager implements X509TrustManager {
private X509TrustManager sunJSSEX509TrustManager;
/**
* 初始化数据
*
* @param certPath 证书位置
* @param certPwd 证书使用密码
*/
SSLCert509TrustManager(String certPath, String certPwd) {
ClassPathResource resource = new ClassPathResource(certPath);
if (resource.exists()) {
InputStream inputStream = null;
try {
inputStream = resource.getInputStream();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(inputStream, (null == certPwd) ? null : certPwd.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
trustManagerFactory.init(keyStore);
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
for (int i = 0; i < trustManagers.length; i++) {
if (trustManagers[i] instanceof X509TrustManager) {
sunJSSEX509TrustManager = (X509TrustManager) trustManagers[i];
break;
}
}
inputStream.close();
} catch (KeyStoreException e) {
log.error("restTemplate ssl cert KeyStoreException {}", e);
} catch (CertificateException e) {
log.error("restTemplate ssl cert CertificateException {}", e);
} catch (NoSuchAlgorithmException e) {
log.error("restTemplate ssl cert NoSuchAlgorithmException {}", e);
} catch (IOException e) {
log.error("restTemplate ssl cert IOException {}", e);
} catch (NoSuchProviderException e) {
log.error("restTemplate ssl cert NoSuchProviderException {}", e);
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
try {
sunJSSEX509TrustManager.checkClientTrusted(x509Certificates, s);
} catch (CertificateException e) {
log.error("checkClientTrusted ssl certException {}", e);
}
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
try {
sunJSSEX509TrustManager.checkServerTrusted(x509Certificates, s);
} catch (CertificateException e) {
log.error("checkServerTrusted ssl certException {}", e);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return sunJSSEX509TrustManager.getAcceptedIssuers();
}
}
}
绕过证书发起https访问,主要是设置信任所有链接
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
/**
* HTTPS请求 = 超文本传输协议HTTP + 安全套接字层SSL 不自主携带证书请求访问
*
* @author Yang douya
* @date 2021/2/3 10:53
*/
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// 强制使用 TLSv1.2 解决 Received fatal alert: protocol_version; nested exception is javax.net.ssl.SSLException: Received fatal alert: protocol_version 异常情况
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, trustAllCerts, new SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* SSLSocketFactory用于创建 SSLSockets
*/
private class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
// 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
// 这些默认的服务的最低质量要求保密保护和服务器身份验证
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
// 返回的密码套件可用于SSL连接启用的名字
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"});
return socket;
}
}
}
封装请求工具RestTemplateUtil由于可能对接是多个平台的API,要携带不同cer证书,或者是不需要证书
import com.alibaba.fastjson.JSONObject;
import com.zyxx.rrp.pojo.ResponseMessage;
import com.zyxx.utils.http.ssl.HttpsClientRequestFactory;
import com.zyxx.utils.http.ssl.HttpsClientRequestFactory2SSLCert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* http或者https请求
*
* @author Yang douya
* @date 2021/2/3 10:23
*/
@Slf4j
public class RestTemplateUtil {
/**
* POST请求
*/
public static final String REQUEST_METHOD_POST = "post";
/**
* get 请求
*/
public static final String REQUEST_METHOD_GET = "get";
private static RestTemplate restTemplate = null;
/**
* 获取 RestTemplate
*
* @param requestUrl 请求路径
* @param useSSl 是否携带自定义证书
* @param certPath 证书位置 useSSl-true 有效
* @param certPassword 证书密码 useSSl-true 有效
* @return
*/
private static RestTemplate getRestTemplate(String requestUrl, boolean useSSl, String certPath, String certPassword) {
if (requestUrl.startsWith("http:")) {
restTemplate = new RestTemplate();
} else if (requestUrl.startsWith("https:")) {
if (useSSl) {
restTemplate = new RestTemplate(new HttpsClientRequestFactory2SSLCert(certPath, certPassword));
} else {
restTemplate = new RestTemplate(new HttpsClientRequestFactory());
}
}
if (null != restTemplate && !ObjectUtils.isEmpty(restTemplate.getMessageConverters())) {
// 强制处理字符集 utf-8
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
HttpMessageConverter<?> httpMessageConverter = null;
for (int i = 0, size = messageConverters.size(); i < size; i++) {
httpMessageConverter = messageConverters.get(i);
if (httpMessageConverter instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);
messageConverters.set(i, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
}
}
return restTemplate;
}
/**
* 默认请求类型 Content-Type:application/x-www-form-urlencoded
*
* @param requestUrl 路径
* @param useSSl 是否携带自定义证书
* @param certPath 证书位置 useSSl-true 有效
* @param certPassword 证书密码 useSSl-true 有效
* @param method 请求方式
* @param paramData 请求参数
* @return 0-失败 1-成功 msgData-响应数据
*/
public static ResponseMessage sendDefault(String requestUrl, boolean useSSl, String certPath, String certPassword, String method, Map<String, String> paramData) {
ResponseMessage responseMessage = new ResponseMessage();
try {
String responseData = "";
if (REQUEST_METHOD_POST.equals(method.toLowerCase())) {
HttpHeaders headers = new HttpHeaders();
// HttpMethod httpMethod = HttpMethod.POST;
// 以表单的方式提交
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
if (null != paramData && 0 < paramData.size()) {
Set<Map.Entry<String, String>> entries = paramData.entrySet();
for (Map.Entry<String, String> entry : entries) {
multiValueMap.add(entry.getKey(), entry.getValue());
}
}
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<MultiValueMap<String, String>>(multiValueMap, headers);
ResponseEntity<String> stringResponseEntity = getRestTemplate(requestUrl, useSSl, certPath, certPassword).postForEntity(requestUrl, httpEntity, String.class);
if (null != stringResponseEntity) {
responseData = stringResponseEntity.getBody();
}
} else if (REQUEST_METHOD_GET.equals(method.toLowerCase())) {
StringBuilder builder = new StringBuilder();
builder.append(requestUrl).append("?");
for (String key : paramData.keySet()) {
builder.append(key).append("=").append(paramData.get(key)).append("&");
}
responseData = getRestTemplate(requestUrl, useSSl, certPath, certPassword).getForObject(builder.toString(), String.class);
}
responseMessage.setCode(1);
responseMessage.setMsg("success");
responseMessage.setData(responseData);
} catch (Exception e) {
responseMessage.setCode(0);
log.error("RestTemplateUtil sendDefault 添加请求异常 {}", e);
}
return responseMessage;
}
/**
* 请求提交json数据 post请求
*
* @param requestUrl 路径
* @param useSSl 是否携带自定义证书
* @param certPath 证书位置 useSSl-true 有效
* @param certPassword 证书密码 useSSl-true 有效
* @param paramData 请求参数
* @return 0-失败 1-成功 msgData-响应数据
*/
public static ResponseMessage sendForJson(String requestUrl, boolean useSSl, String certPath, String certPassword, Object paramData) {
ResponseMessage responseMessage = new ResponseMessage();
HttpHeaders headers = new HttpHeaders();
// 消息格式
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> formEntity = new HttpEntity<>(JSONObject.toJSONString(paramData), headers);
try {
String responseData = getRestTemplate(requestUrl, useSSl, certPath, certPassword).postForEntity(requestUrl, formEntity, String.class).getBody();
responseMessage.setCode(1);
responseMessage.setMsg("success");
responseMessage.setData(responseData);
} catch (Exception e) {
responseMessage.setCode(0);
log.error("RestTemplateUtil sendForJson请求异常 {}", e);
}
return responseMessage;
}
/**
* 请求提交json数据 post请求
*
* @param requestUrl 路径
* @param useSSl 是否携带自定义证书
* @param certPath 证书位置 useSSl-true 有效
* @param certPassword 证书密码 useSSl-true 有效
* @param headerMap 消息头
* @param paramData
* @return 0-失败 1-成功 msgData-响应数据
*/
public static ResponseMessage sendForJson(String requestUrl, boolean useSSl, String certPath, String certPassword, Map<String, String> headerMap, Object paramData) {
ResponseMessage responseMessage = new ResponseMessage();
HttpHeaders headers = new HttpHeaders();
// 消息格式
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
if (null != headerMap && 0 < headerMap.size()) {
Set<Map.Entry<String, String>> entries = headerMap.entrySet();
for (Map.Entry<String, String> map : entries) {
headers.add(map.getKey(), map.getValue());
}
}
HttpEntity<String> formEntity = new HttpEntity<>(JSONObject.toJSONString(paramData), headers);
try {
String responseData = getRestTemplate(requestUrl, useSSl, certPath, certPassword).postForEntity(requestUrl, formEntity, String.class).getBody();
responseMessage.setCode(1);
responseMessage.setMsg("success");
responseMessage.setData(responseData);
} catch (Exception e) {
responseMessage.setCode(0);
log.error("RestTemplateUtil sendForJson请求异常 {}", e);
}
return responseMessage;
}
/**
* 请求提交json数据 post请求
*
* @param requestUrl 路径
* @param useSSl 是否携带自定义证书
* @param certPath 证书位置 useSSl-true 有效
* @param certPassword 证书密码 useSSl-true 有效
* @param xml xml数据
* @return 0-失败 1-成功 msgData-响应数据
*/
public static ResponseMessage sendForXml(String requestUrl, boolean useSSl, String certPath, String certPassword, String xml) {
ResponseMessage responseMessage = new ResponseMessage();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.add("Accept", MediaType.APPLICATION_XML_VALUE);
HttpEntity<String> formEntity = new HttpEntity<>(xml, headers);
try {
String responseData = getRestTemplate(requestUrl, useSSl, certPath, certPassword).postForEntity(requestUrl, formEntity, String.class).getBody();
responseMessage.setCode(1);
responseMessage.setMsg("success");
responseMessage.setData(responseData);
} catch (Exception e) {
responseMessage.setCode(0);
log.error("RestTemplateUtil 添加请求异常 {}", e);
}
return responseMessage;
}
/**
* get请求
*
* @param useSSl 是否携带自定义证书
* @param certPath 证书位置 useSSl-true 有效
* @param certPassword 证书密码 useSSl-true 有效
* @param requestUrl
* @return
*/
public static ResponseMessage sendForGet(String requestUrl, boolean useSSl, String certPath, String certPassword) {
ResponseMessage responseMessage = new ResponseMessage();
try {
ResponseEntity<String> forEntity = getRestTemplate(requestUrl, useSSl, certPath, certPassword).getForEntity(requestUrl, String.class);
if (null != forEntity) {
String responseData = forEntity.getBody();
responseMessage.setCode(1);
responseMessage.setMsg("success");
responseMessage.setData(responseData);
//log.info("get 请求响应数据 {}", responseData);
}
} catch (Exception e) {
log.error("get 请求异常 url {} 异常 {}", requestUrl, e);
}
return responseMessage;
}
}
使用工具即可