1. 简介

Apache HttpClient 是一个轻量、低级别的客户端 HTTP 库,用于与 HTTP 服务器进行通信。本文将介绍在使用 HttpClient 时如何配置所支持的传输层安全(TLS)版本。

我们将从 TLS 版本协商机制讲起,然后重点介绍 三种不同的方式来配置 HttpClient 所支持的 TLS 版本。在第 3 节中,我们还会说明 Apache HttpClient 4 在静态设置 TLS 配置方面的一些差异。

2. Apache HttpClient 5

2.1 TLS 版本协商机制

TLS 是一种互联网协议,用于在两个通信方之间建立安全、可信的连接。它封装了应用层协议(如 HTTP)。自 1999 年首次发布以来,TLS 协议已经经历了多次修订。因此,客户端和服务器在建立连接前,必须先协商好使用哪个版本的 TLS 协议

TLS 版本协商过程如下:

  1. 客户端发送其支持的 TLS 版本列表;
  2. 服务器从中选择一个版本,并在响应中告知客户端;
  3. 双方使用选定的版本继续建立连接。

正确配置客户端支持的 TLS 版本非常重要,可以有效防范 降级攻击。⚠️ 注意:若要使用最新的 TLS 1.3,需要 Java 11 或更高版本。

2.2 静态配置 TLS 版本

2.2.1 使用 HttpClientConnectionManager

首先,我们创建一个自定义 TLS 配置的连接管理器,然后将其设置给通过 HttpClients.custom() 构建的 ClosableHttpClient 实例:

final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
  .setDefaultTlsConfig(TlsConfig.custom()
     .setHandshakeTimeout(Timeout.ofSeconds(30))
     .setSupportedProtocols(TLS.V_1_2, TLS.V_1_3)
      .build())
   .build();

return HttpClients.custom().setConnectionManager(cm).build();

✅ 返回的 HttpClient 对象现在只会支持 TLS 1.2 和 TLS 1.3 的通信。

2.2.2 使用 Java 运行时参数

另一种方式是通过 Java 的 https.protocols 系统属性来配置支持的 TLS 版本,避免硬编码在代码中。我们可以让 HttpClient 使用系统属性来配置连接:

CloseableHttpClient httpClient = HttpClients.createSystem();

如果需要更多自定义配置,也可以使用构建器方式:

CloseableHttpClient httpClient = HttpClients.custom().useSystemProperties().build();

这两种方式都会让 HttpClient 在连接时读取系统属性。例如,我们可以通过以下命令启动应用:

$ java -Dhttps.protocols=TLSv1.1,TLSv1.2,TLSv1.3 -jar webClient.jar

2.3 动态配置 TLS 版本

我们也可以根据连接的主机名或端口动态设置 TLS 版本。做法是继承 SSLConnectionSocketFactory 并重写 prepareSocket 方法。该方法在建立连接前被调用,从而允许我们按连接动态选择 TLS 版本

示例:仅对特定子域名启用旧版 TLS:

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(SSLContexts.createDefault()) {

    @Override
    protected void prepareSocket(SSLSocket socket) {

        String hostname = socket.getInetAddress().getHostName();
        if (hostname.endsWith("internal.system.com")) {
            socket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" });
        } else {
            socket.setEnabledProtocols(new String[] {"TLSv1.3"});
        }
    }
};
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

✅ 上述代码中,prepareSocket 方法会根据目标主机名决定启用哪些 TLS 协议。除了匹配 *.internal.system.com 的主机外,其他请求都强制使用 TLS 1.3。

3. Apache HttpClient 4

3.1 静态配置 TLS 版本

3.1.1 使用 SSLConnectionSocketFactory

在 HttpClient 4 中,我们可以使用 HttpClients.custom() 返回的 HttpClientBuilder 来自定义配置,并传入一个自定义的 SSLConnectionSocketFactory 实例:

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
  SSLContexts.createDefault(),
  new String[] { "TLSv1.2", "TLSv1.3" },
  null,
  SSLConnectionSocketFactory.getDefaultHostnameVerifier());

CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

✅ 此方式下,HttpClient 只支持 TLS 1.2 和 TLS 1.3。⚠️ 注意:在 HttpClient 4.3 之前的版本中,该类名为 SSLSocketFactory

4. 总结

本文介绍了在使用 Apache HttpClient 时配置 TLS 版本的三种方式,包括:

  • 全局静态配置(如通过连接管理器或系统属性)
  • 动态配置(基于连接目标的自定义逻辑)

通过这些方法,我们可以灵活控制客户端所支持的 TLS 版本,提升通信安全性。✅ 推荐在生产环境中强制使用 TLS 1.2 或更高版本,避免兼容过旧协议带来的安全风险。


原始标题:How to Set TLS Version in Apache HttpClient | Baeldung