OkHttpClient在发送https请求时忽略认证
背景
当在公司服务器上调用第三方的https接口时可能会报如下错误:PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
,可以将该接口对应站点的证书下载下来然后导入到服务器的jdk受信任证书中(但证书有效期过了之后,要记得重新导入证书),也可以在用OkHttpClient发送https请求时通过程序来忽略这个认证。我通过第二种方案来解决这个问题。
解决方法
依赖的相关jar包可以通过maven的pom配置文件来引入:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.4</version>
</dependency>
部分核心代码如下:
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class OkHttpUtil {
private final static int READ_TIMEOUT = 30;
private final static int CONNECT_TIMEOUT = 20;
private final static int WRITE_TIMEOUT = 20;
public static OkHttpClient.Builder buildOKHttpClient() {
try {
TrustManager[] trustAllCerts = buildTrustManagers();
final SSLContext sslContext = SSLContext.getInstance("SSL");//与服务器保持一致,SSL算法或者TSL算法
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier((hostname, session) -> true);//添加支持https的支持,这么写表示支持所有类型的https请求 lambda表达式需要jdk1.8+
/* 这种写法和上面一行效果一样,非lambda表达式,jdk1.8以下可以用
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
*/
return builder;
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
return new OkHttpClient.Builder();
}
}
private static TrustManager[] buildTrustManagers() {
return new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
}
/**
* @ Deprecated :map转为string
* @param pmap
* @return key=value&key=value...格式的一个字符串
*/
public static String mapToString(Map<String, Object> pmap) {
String postpar = "";
// map格式的请求参数
if(pmap!=null) {
StringBuffer mstr = new StringBuffer();
for (String str : pmap.keySet()) {
String val = (String) pmap.get(str);
try {
val = URLEncoder.encode(val, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mstr.append(str + "=" + val + "&");
}
postpar = mstr.toString();
int lasts=postpar.lastIndexOf("&");
postpar=postpar.substring(0, lasts);
}
return postpar;
}
static Response response;
/**
* @ Deprecated :发送请求
* @param json 要传递的数据 一般都是json格式
* @param url 调用接口的地址
* @param pmap 和接口地址要拼接到一起的一些参数信息,比如https://xxxxxx?appId=xxx&key=xxx...
* @return
*/
public static String sendByHttps(String json, String url,Map<String,Object> pmap) {
String respContent = null;
try{
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
String pString = mapToString(pmap);
if(pString!=null&pString.length()>0){
url+="?"+pString;
}
Request request = new Request.Builder()
.url(url)
.headers(Headers.of(new HashMap<>()))
.post(RequestBody.create(mediaType, json))
.build();
//OkHttpClient是通过OkHttpClient.Builder来配置参数的,超时时间的限制根据自己项目自行修改
OkHttpClient okHttpClient = buildOKHttpClient()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
.build();
response = okHttpClient.newCall(request).execute();
if(response.code()==200){
ResponseBody respBody = response.body();
respContent = respBody==null ? null:respBody.string();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(response!=null){
response.close();
}
}
return respContent;
}
public static void main(String[] args) {
//参数封装 会和url拼接到一起
HashMap<String, Object> pmap = new HashMap<>();
pmap.put("key1","value1");
pmap.put("key2","value2");
String url = "";//接口请求地址
String json = "";//请求接口时传递的数据
String s = sendByHttps(json, url, pmap);
System.out.println(s);
}
}