问题描述
支付宝互联网地址为 https://openapi.alipay.com/gateway.do
,专线相比于互联网地址速度更快,更加稳定。这里我们假设专线地址为 https://xxx.xxx.xxx.xxx:443/gateway.do
。
我们通过支付宝的 SDK 来访问支付宝专线地址,结果报了以下错误
level:ERROR messasge:AbsAlipayService.getResponse.AlipayApiException stackTrack:"com.alipay.api.AlipayApiException: java.io.IOException: HTTPS hostname wrong: should be <xxx.xxx.xxx.xxx>
at com.alipay.api.AbstractAlipayClient.doPost(AbstractAlipayClient.java:692)
at com.alipay.api.AbstractAlipayClient._execute(AbstractAlipayClient.java:607)
at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:108)
at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:95)
at com.alipay.api.AbstractAlipayClient.execute(AbstractAlipayClient.java:89)
问题原因
支付宝 SDK 底层使用 WebUtils 来发送网络请求,HostnameVerifier 实现类默认返回 false,我们需要跳过此验证。
verifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return false;
}
};
解决方法
方法1
private static void skipAlipayHttpsVerifier() {
try {
Field verifierField = WebUtils.class.getDeclaredField("verifier");
verifierField.setAccessible(true);
verifierField.set(null, new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
} catch (Throwable e) {
e.printStackTrace();
}
}
在项目启动时调用此方法,通过反射来重写 HostnameVerifier 的实现类。
方法2
public abstract class WebUtils {
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String METHOD_POST = "POST";
private static final String METHOD_GET = "GET";
private static SSLContext ctx = null;
private static HostnameVerifier verifier = null;
private static SSLSocketFactory socketFactory = null;
private WebUtils() {
}
static {
try {
ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[]{new DefaultTrustManager()}, new SecureRandom());
ctx.getClientSessionContext().setSessionTimeout(15);
ctx.getClientSessionContext().setSessionCacheSize(1000);
socketFactory = ctx.getSocketFactory();
} catch (Exception var1) {
}
verifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
// 仅修改这一个地方
return true;
}
};
}
}
在项目中同一个 package 下创建一个 WebUtils,具体类路径为 com.alipay.api.internal.util.WebUtils
,这样类加载器最终加载的就是我们自己定义的 WebUtils 类了。