赞
踩
最近在对接业务的时候,需要请求云上厂家的接口。
首先说明一点,云是属于内网环境的,且调用是HTTPS协议的,所以厂家使用到了自签证书。而自签证书在对于不处于同一内部网络的机器,在进行HTTPS请求的时候,就会遇到SSL验证无法通过的问题,如以下。
curl: (35) OpenssL SSL connect: Connection reset by peer in connection to ip:port
第一步
你要确定的是,你的请求只是因为绕不过去SSL验证。如果你中间还套了一层网闸,或者其他网络转发的话,那么即时你使用下方的解决思路,也是错误的。因为很有可能你的本机地址映射出去的IP并不是你用 ip addr
命令输出的地址,这点你要确定好。
第二步
非常重要的事,使用telnet ip port
,测试网络连接情况,如果能够正常建立起连接,那么说明你的请求是可以出去你所处的云网络的。
第三步
简单测试,使用curl命令进行排障,先把接口使用postman等工具编辑好后生成curl语句,并且在生成的语句内添加上 -k
或者 --insecure
,如果能正常返回业务结果,那么就可以开始尝试写绕过SSL验证的代码了。
具体代码下方已经给出。
工具类:
import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; public class SslUtils { private static void trustAllHttpsCertificates() throws Exception { TrustManager[] trustAllCerts = new TrustManager[1]; TrustManager tm = new miTM(); trustAllCerts[0] = tm; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } static class miTM implements TrustManager,X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public boolean isServerTrusted(X509Certificate[] certs) { return true; } public boolean isClientTrusted(X509Certificate[] certs) { return true; } public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } } /** * 忽略HTTPS请求的SSL证书 */ public static void ignoreSsl() throws Exception{ HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String urlHostName, SSLSession session) { log.debug("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost()); return true; } }; trustAllHttpsCertificates(); HttpsURLConnection.setDefaultHostnameVerifier(hv); } }
POST请求方法:
import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLConnection; public class SolutionServiceImpl { //post 请求,自动绕开SSL验证 public String postRequest(String urlAddress,String args,int timeOut) throws Exception{ URL url = new URL(urlAddress); if("https".equalsIgnoreCase(url.getProtocol())){ SslUtils.ignoreSsl();//忽略掉SSL异常 } URLConnection u = url.openConnection(); u.setRequestProperty("Content-Type", "application/json"); u.setRequestProperty("Authorization", "Bearer *****"); //设置请求头 u.setDoInput(true); u.setDoOutput(true); u.setConnectTimeout(timeOut); u.setReadTimeout(timeOut); OutputStreamWriter osw = new OutputStreamWriter(u.getOutputStream(), "UTF-8"); osw.write(args); osw.flush(); osw.close(); u.getOutputStream(); return IOUtils.toString(u.getInputStream()); } }
写一个Api结果构造类:
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ApiResultVO<T> {
private String code;
private String msg;
private List<T> data;
}
使用方法:
import cn.hutool.core.lang.TypeReference; private static final String baseUrl = "https://ip:port/api"; //接口偏移 private static final String requestUrl = "/requestData"; private static Integer timeout = 10000; public class SolutionServiceImpl { public Map<String,Object> getAlarmJqListByTime(RequestForm form) throws Exception { String apiresult = postRequest(baseUrl+requestUrl, parseJSON(form), timeout); ApiResultVO<APIVO> resList = JSONUtil.toBean(apiresult,new TypeReference<ApiResultVO<APIVO>>(){},false);//ApiResultVO使用的是泛型,APIVO就是你的实体类了 /// /// } private String parseJSON(RequestForm form) { JSONObject param = new JSONObject(); param.put("param",form.getParam()); return param.toString(); } }
有一种情况,是某个机构已经把你的云主机IP映射到接口厂家的同一片网络上,但这并不代表你就能直接HTTPS请求对方的接口,这一点是需要注意的。当然这中间怎么做的网络映射,笔者也没有完全了解,可能观点是片面的,欢迎指点。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。