Http请求

243 阅读2分钟

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);

    }
}