最近在部署应用的时候,代码几乎没有太大改动。结果报了如下错误【clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 9+】:
于是紧急回滚,但错误依旧存在。在参考了博客后,发现可能原因是jdk环境发生了改变。于是迅速查看jdk版本,发现云平台运维近期将jdk版本由1.8.0_232升级到了1.8.0_252。但当时也有一批微服务用到了相同版本的okhttp,却没有报相应的错误。在升级okhttp版本暂时规避了这个问题后,最近也抽时间对此进行了研究。
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方法:
这样就不会报错了。
Okhttp 4.X版本采用kotlin语言进行了编写,整体思路和Java版本区别不大。我们首先来看看okhttp是如何改进这个bug的:
我们在来看看新版本是如何判断jdk9版本的:
采用了从系统读取Java的主版本号,判断是否大于9来实现的。
我们看到2.2节中sslSocketFactory方法标记了@deprecated注解,重载的方法为:
重载方法将信任管理设为了输入参数。而且给出了使用建议:
其实就是调用sslSocketFactory时候传入trustManager。(推荐此做法,无论okhttp、okhttp版本是什么,都不会报错)。
针对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版本是什么,都不会报错)。