原本已正常上线两周多的功能,突然一天客户反映说无法访问了。
查看日志发现调用第三方接口获取accesstoken的时候发生了IoExpection,具体异常信息如下:
get token error:javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
自己代码本身没有更新,所以第一时间想到是第三方接口异常了,
于是乎询问第三方该接口目前是否正常,对方测试了下,在浏览器了发起get请求能正常返回结果。于是想到应该是后台https接口调用的时候发生了异常。
贴出原始代码:
public class HttpClientUtils {
private static final String HTTPS = "https";
private static HttpClient httpClient;
static {
PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
cm.setMaxTotal(50);
cm.setDefaultMaxPerRoute(50);
cm.getSchemeRegistry().register(new Scheme(HTTPS, 443, getSSLSocketFactory()));
DefaultHttpClient defaultHttpClient = new DefaultHttpClient(cm);
defaultHttpClient.getParams().setIntParameter("http.socket.timeout", 10000);
httpClient = defaultHttpClient;
}
private static SSLSocketFactory getSSLSocketFactory() {
SSLSocketFactory.getSocketFactory();
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {return null;}
};
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory socketFactory = new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
return socketFactory;
}
catch (Exception e){
throw new IllegalStateException(e);
}
}
public static String doGet(String url) throws IOException {
HttpGet get = new HttpGet(url);
try {
HttpResponse response = httpClient.execute(get);
String res;
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode < 300){
HttpEntity entity = response.getEntity();
res = EntityUtils.toString(entity);
EntityUtils.consume(entity);
}
else{
throw new IOException("http get[" + url + "] failed,statuCode [" + statusCode + "].");
}
return res;
}
catch (Exception e){
if(!get.isAborted()) {
get.abort();
}
throw new IOException(e);
}
}
}
上面的https调用是信任所有证书的,且使用的是TLS协议。这时,我在我本地重新调用接口测试正常。但是服务器上却发生异常,想到应该是环境不同问题,我本地的JDK8,服务器是是JDK7。难道还跟JDK版本有关?这时我询问第三方是否有做什么改动,
对方回复
妮玛,果然是对方改动引起的。
于是网上查了资料并经过测试发现,第三方这个接口 jdk1.7 得明确指定TLSv1.2 才能调通。其他版本都不行,TLSv1, TLSv.1.1不通。jdk7默认好像是使用TLSv1。如果使用jdk8 是没有问题的,默认就是使用TLSv1.2 。
针对jdk7 需要做以下改动就可以,
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
下面是paypal TLS1.2升级说明,为我这次修改提供了解决思路。https://github.com/paypal/TLS-update
有感
对于第三方厂商或者自己平台,如果对接口协议做升级,需要提前公告通知,并提供升级解决方案说明,不然如果单方面升级,你的客户就要找上你了。