MTLS和http客户端连接池的使用

问题描述

背景

  • 我需要为各种客户端连接到服务器。
  • 每个客户端连接应使用唯一的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

解决方法

这个假设是真的吗?

您的假设是正确的。用于requestingreleasing连接的连接池的方法都接受一个名为 state 的附加参数。此 state 参数通常使用用户令牌,如果没有身份验证,则使用null

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

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

场景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);

请注意,如果会话超时或请求重新协商,即使重新使用连接,也可能需要完整的握手。

,

这个假设是真的吗?

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

场景1)

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

方案2)

不,不是。请提供出现问题的会话的 complete 上下文/联系日志,我将尝试找出原因。

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...