升级 JDK 17 碰到的请求 https 问题

发布于:2025-08-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

问题

升级 JDK 17 后,一个自定义请求,提示 The server selected protocol version TLS10 is not accepted的异常,这个问题,在之前应该是碰到过的,所以有记录了对应的链接,https://cloud.tencent.com/developer/article/2127522。但这次在客户那部署,虽然调整了对应的参数,但依然还是请求失败。趁着周末重新验证了这个 TLSv1 的请求。

在这里插入图片描述

处理

解封 TLSv1

因为 TLSv1,TLSv1.1 有已知的安全漏洞,所以在高版本的 JDK里面默认的禁用了 TLSv1,TLSv1.1 的安全算法。通过whereis 和 ll 等命令可以定位 JAVA_HOME 目录(也可以直接尝试 echo JAVAHOME看看是否有配置对应的目录),接着定位到‘JAVA_HOME 看看是否有配置对应的目录),接着定位到`JAVAHOME看看是否有配置对应的目录),接着定位到JAVA_HOME/conf/security/java.security` 文件。

找到 jdk.tls.disabledAlgorithms部分,旧内容如下:(不同JDK 版本可能略有不同)

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL

去除 TLSv1, TLSv1.1, ,修改如下:

jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL

如果不想修改这个配置,也可以尝试的程序运行时指定参数。

java -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2,TLSv1.3" -Djdk.tls.server.protocols="TLSv1,TLSv1.1,TLSv1.2,TLSv1.3" -jar app.jar

通常情况下,此时应该能正常使用,但发到现场后发现还是上面的错误。

模拟环境

从提示中我们可以看到,应该是对方的https 服务使用的是 TLSv1 版本的协议,于是修改自己的 nginx 服务强制指定ssl_protocols 为 TLSv1;

        ssl_protocols TLSv1;
        ssl_ciphers  HIGH:!aNULL:!MD5;

此时浏览器范围页面也是异常的。

在这里插入图片描述

此时通过程序测试,提示的异常虽然有些不同,但基本可以看出是同一个问题引起的了。

在这里插入图片描述

尝试切换 hutool 的 httputil

切换 httputil 后发现服务请求自定义证书,提示下面的错误。

在这里插入图片描述

经过一番查看源代码后,发现 hutool 支持设置一个全局参数HttpGlobalConfig.setTrustAnyHost(true),设置了全局参数之后发现,请求正常了。

在这里插入图片描述

为什么 httputils 不行呢?

一开始以为是创建的 SSLContext 不同,更换成 hutool 工具创建的 SSLContext SSLContextUtil.createTrustAnySSLContext 依然还是不行。于是对比了下 hutool 和 solon 的是请求默认的类库不同,hutool 默认使用的是httpclient4,solon默认使用的是okhttp。当把 hutool 的默认请求切换到 okhttp 时,虽然一样也设置了*setTrustAnyHost*(true),但请求一样是报错的。

接着跟踪到 hutool 中 httpclient4 里面的一个关键设置。

private static SSLConnectionSocketFactory buildSocketFactory(SSLInfo sslInfo) {
    return null == sslInfo ? SSLConnectionSocketFactory.getSocketFactory() : new SSLConnectionSocketFactory(sslInfo.getSslContext(), sslInfo.getProtocols(), (String[])null, sslInfo.getHostnameVerifier());
}

此时可以比较明确的确定是连接客户也需要设置的问题,于是找到 OkHttpClient 创建的位置,增加设置connectionSpecs

在这里插入图片描述

solon 作者说按上述设置会导致 http 无法访问,同时还发现 okhttp 还提供了一些常量,换成如下写法兼容性更好。

connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT))

临时处理

扩展 OkHttpUtilsFactory
public class OkHttpUtilsFactoryExt extends OkHttpUtilsFactory {
  static final Logger log = LoggerFactory.getLogger(OkHttpUtilsFactory.class);
  private static final OkHttpUtilsFactory instance = new OkHttpUtilsFactory();
  private static final OkHttpDispatcher dispatcher = new OkHttpDispatcher();
  private final OkHttpClient defaultClient = createHttpClient(null, null);

  private static OkHttpClient createHttpClient(Proxy proxy, HttpSslSupplier sslProvider) {
    if (sslProvider == null) {
      sslProvider = HttpSslSupplierDefault.getInstance();
    }

    OkHttpClient.Builder builder =
        (new OkHttpClient.Builder())
            .connectTimeout(10L, TimeUnit.SECONDS)
            .writeTimeout(60L, TimeUnit.SECONDS)
            .readTimeout(60L, TimeUnit.SECONDS)
            .dispatcher(dispatcher.getDispatcher())
            .addInterceptor(OkHttpInterceptor.instance)
            .sslSocketFactory(sslProvider.getSocketFactory(), sslProvider.getX509TrustManager())
            .connectionSpecs(Arrays.asList(ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT))
            .hostnameVerifier(sslProvider.getHostnameVerifier());

    if (proxy != null) {
      builder.proxy(proxy);
    }

    return builder.build();
  }

  @Override
  protected OkHttpClient getClient(Proxy proxy, HttpSslSupplier sslProvider) {
    return createHttpClient(proxy, sslProvider);
  }
}
扩展 HttpSslSupplier
@Component
public class ExtensionHttp extends HttpSslSupplierDefault
    implements HttpExtension, HttpSslSupplier {
  // for HttpSslSupplier
  private SSLContext sslContext;
  private String[] protocols;

  // for HttpExtension
  @Override
  public void onInit(HttpUtils httpUtils, String url) {
    httpUtils.ssl(this);
  }

  @Override
  public SSLContext getSslContext() {
    return SSLContextUtil.createTrustAnySSLContext();
  }
}
使用

如果指定了 HttpExtension的扩展为 Component,默认情况是会自动注册扩展的,测试时可能需要自己通过addExtension 的方式添加扩展。

HttpConfiguration.setFactory(new OkHttpUtilsFactoryExt());
HttpConfiguration.addExtension(new ExtensionHttp());

正式处理

等待 solon 3.5.0 版本的发布,之后切换 SSLContextUtil.createTrustAnySSLContext(),替换默认的SSLContext。


网站公告

今日签到

点亮在社区的每一天
去签到