okhttp与jdk版本不兼容分析

1、背景

最近在部署应用的时候,代码几乎没有太大改动。结果报了如下错误【clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 9+】:

于是紧急回滚,但错误依旧存在。在参考了博客后,发现可能原因是jdk环境发生了改变。于是迅速查看jdk版本,发现云平台运维近期将jdk版本由1.8.0_232升级到了1.8.0_252。但当时也有一批微服务用到了相同版本的okhttp,却没有报相应的错误。在升级okhttp版本暂时规避了这个问题后,最近也抽时间对此进行了研究。

2、排查调用过程

1. 代码中okhttp的trustHttps字段设置是true,代表信任所有的https连接。具体实现时:

2. OkHttpClient.class中关于sslSocketFactory方法如下:

3.  Platform.class中关于findPlatform方法如下:

4.  Jdk9Platform是Platform的子类。Jdk9Platform.class中buildIfSupported方法如下:

可以看出来,3.x版本的okhttp中通过jdk中SSLParameters.class中包含setApplicationProtocols方法、SSLSocket.class中包含getApplicationProtocol来确定是jdk1.9以上版本。但在jdk 1.8.0_251之后的版本中都包含这两个方法。4.3.0版本以下的okhttp都会对这个误判。返回Jdk9Platform对象,步骤3直接返回jdk9对象。而在步骤2调用其trustManager方法时,由于父类和子类都包含trustManager方法,返回对象是子类,故会调用子类的实现(多态)。子类实现如下:

因此抛出不支持的异常。

5. 在jdk1.8.0_251之前的版本中在步骤3 会直接通过new Platform()创建父类对象,之后就会调用父类中的trustManager方法:

这样就不会报错了。

3、 okhttp 4.3.0的改进

Okhttp 4.X版本采用kotlin语言进行了编写,整体思路和Java版本区别不大。我们首先来看看okhttp是如何改进这个bug的:

我们在来看看新版本是如何判断jdk9版本的:

采用了从系统读取Java的主版本号,判断是否大于9来实现的。

4、 重载方法

我们看到2.2节中sslSocketFactory方法标记了@deprecated注解,重载的方法为:

重载方法将信任管理设为了输入参数。而且给出了使用建议:

其实就是调用sslSocketFactory时候传入trustManager。(推荐此做法,无论okhttp、okhttp版本是什么,都不会报错)。

5、 总结

针对jdk 1.8.0_251、okhttp4.3.0以下版本不兼容问题,报如下错误【clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 9+】

解决办法1:升级okhttp4.3.0以上版本或降级至jdk1.8.0_251之前版本。

解决办法2:调用sslSocketFactory时候传入trustManager。(推荐此做法,无论okhttp、okhttp版本是什么,都不会报错)。