2

背景

  • 我需要为各种客户端连接到服务器。
  • 每个客户端连接都应使用唯一的 TLS 证书。
  • MTLS 在服务器上就位。
  • 我想使用连接池来改善延迟。

使用以下 http 客户端

<dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.5.12</version>
</dependency>

我的假设 在管理连接池中的连接时,在选择连接时,应在选择连接池中的相同连接之前考虑客户端证书。我不希望使用 clientB TLS 证书与 clientA 建立连接,反之亦然。

问题 这个假设是真的吗?

场景 1)

我将每个路由的最大连接数设置为 2。我为客户端 A 调用 MTLS 安全服务器。(池中的一个连接)我为客户端 A 调用 MTLS 安全服务器。(池中的两个连接)这不应该重用第一个连接吗?

方案 2)

我将每个路由的最大连接数设置为 2。我为客户端 A 调用 MTLS 安全服务器。(池中的一个连接)我为客户端 B 调用 MTLS 安全服务器。(池中的两个连接)

但是,第二次调用似乎没有进行完整的握手,而是使用了 clientA 证书。

我希望第二次通话需要完整的握手,并且无论如何连接都不会相关。

这是预期的行为吗?我在这里遗漏了一些明显的东西吗?

更新了更简单的测试用例

我们现在使用的是 http 上下文,所以我附上了更新的日志。我也简化了测试用例,现在它每次应该使用不同的客户端证书时连接到同一个服务器两次。

该应用程序正在使用 spring boot,并且有一个 restTemplate 和一个 httpClient。

它使用 PrivateKeyStrategy 来决定在与服务器通信时使用什么私钥/证书。

第一个连接使用密钥别名“e2e_transport_key_id_franek”(您将在日志中看到这一点)

第二个连接应该使用别名'e2e_transport_key_id_pdw'(在日志中从未见过)

我们正在建立的第二个连接应该使用别名为“e2e_transport_key_id_pdw”的密钥/证书,但是会话已恢复,请参见第 448 行尝试恢复会话。这意味着我们不能使用 PrivateKeyStrategy 来选择要使用的客户端证书。

如何强制客户端连接不为我们打算使用不同客户端证书的连接重用会话?

客户端日志 https://pastebin.com/zN0EW3Qy

4

2 回答 2

4

这个假设是真的吗?

你的假设是对的。连接池中用于请求释放连接的方法都带有一个名为state的附加参数。此状态参数通常采用用户令牌,或者null如果没有进行身份验证。

仅当使用用于将连接释放回连接池的相同用户令牌请求连接时,才能重新使用连接。

此机制也适用于 SSL 客户端证书。成功的 SSL 握手后,SSL 连接与代表用户令牌的X500Principal一起被释放。此令牌也存储在HttpContext用于请求的对象中。要在后续 HTTP 请求中重新使用已释放的连接,您还必须重新使用HttpContext第一个 HTTP 请求的连接。

方案 1

clientA.execute(new HttpGet("..."));
clientA.execute(new HttpGet("..."));

第一个请求触发完整的 SSL 握手。使用用户令牌释放连接。第二个请求使用HttpContext不包含任何用户令牌的新请求。因此,池中的连接不能被重新使用,并且通过完全握手创建一个新连接。

方案 2

HttpContext context = new HttpClientContext();
clientA.execute(new HttpGet("..."), context);
clientB.execute(new HttpGet("..."), context);

如前所述,用户令牌存储在HttpContext. 因此,如果您重新使用HttpContext第一个请求中的第一个请求,即使您使用具有不同连接工厂/客户端证书的不同客户端,第二个请求也可以重新使用已经存在的连接。如果连接应该在使用中,将使用 clientB 的连接工厂创建一个新连接。

要分离clientAclientB确保仅在每个客户端重复使用连接,您必须为HttpContext每个客户端使用一个并为每个请求重新使用上下文:

HttpContext contextA = new HttpClientContext();
clientA.execute(new HttpGet("..."), contextA);
clientA.execute(new HttpGet("..."), contextA);

HttpContext contextB = new HttpClientContext();
clientB.execute(new HttpGet("..."), contextB);

请注意,在会话超时或请求重新协商的情况下,即使连接被重用,也可能需要完全握手。

于 2020-09-19T13:58:04.390 回答
2

这个假设是真的吗?

你的假设是正确的。HttpClient 4 和 5 能够跟踪与 HTTP 连接(NTLM 上下文、TLS 用户身份等)相关的用户特定状态,并在重用持久连接时将其考虑在内。

场景 1)

后一个调用应该重用现有的连接,只要它与前一个共享相同的执行上下文。

方案 2)

不它不是。请提供显示问题的会话的完整上下文/线路日志,我将尝试找出原因。

http://hc.apache.org/httpcomponents-client-4.5.x/logging.html

于 2020-09-19T10:46:21.320 回答