原本已正常上线两周多的功能,突然一天客户反映说无法访问了。
查看日志发现调用第三方接口获取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版本有关?这时我询问第三方是否有做什么改动,
对方回复

记一次https调用发生SSLHandShakeException异常

妮玛,果然是对方改动引起的。
于是网上查了资料并经过测试发现,第三方这个接口 jdk1.7 得明确指定TLSv1.2 才能调通。其他版本都不行,TLSv1, TLSv.1.1不通。jdk7默认好像是使用TLSv1。如果使用jdk8 是没有问题的,默认就是使用TLSv1.2 。
记一次https调用发生SSLHandShakeException异常

针对jdk7 需要做以下改动就可以,

 SSLContext ctx = SSLContext.getInstance("TLSv1.2");

下面是paypal TLS1.2升级说明,为我这次修改提供了解决思路。https://github.com/paypal/TLS-update

有感

对于第三方厂商或者自己平台,如果对接口协议做升级,需要提前公告通知,并提供升级解决方案说明,不然如果单方面升级,你的客户就要找上你了。

相关文章:

  • 2022-01-31
  • 2022-12-23
  • 2021-07-15
  • 2022-12-23
  • 2020-04-08
  • 2021-05-24
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-05
  • 2022-12-23
  • 2022-01-10
  • 2021-09-25
相关资源
相似解决方案