【问题标题】:How to create a pool of Apache HttpClients with varying SSL client auth certificates如何创建具有不同 SSL 客户端身份验证证书的 Apache HttpClients 池
【发布时间】:2019-08-16 05:50:08
【问题描述】:

我一直在尝试创建一个使用 Apache HttpClient 调用各种 RESTful Web 服务的客户端的 Apache 文档和其他示例。 (这些 Web 服务中的每一个都可能需要不同的客户端证书进行身份验证)。最初,我创建了一个初始化 HttpClient 的静态代码块(带有 SSLContext 信息和池连接管理器):

private static CloseableHttpClient _client;
static {
  HttpClientBuilder clientBuilder = HttpClients.custom();
  SSLContextBuilder sslContextBuilder = SSLContexts.custom();
  sslContextBuilder.loadTrustMaterial(new TrustSelfSignedStrategy());
  sslContextBuilder.loadKeyMaterial(new File("clientcert.p12"), password, password, (aliases, socket) -> aliases.keySet().iterator().next());

  SSLContext sslContext = sslContextBuilder.build();
  HostnameVerifier allowAllHosts = new NoopHostnameVerifier();
  SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, allowAllHosts);
  clientBuilder.setSSLSocketFactory(connectionFactory);

  RegistryBuilder<ConnectionSocketFactory> regBuilder = RegistryBuilder.<ConnectionSocketFactory>create();
  regBuilder.register("https", connectionFactory);
  regBuilder.register("http", new PlainConnectionSocketFactory());
  Registry<ConnectionSocketFactory> socketFactoryRegistry = regBuilder.build();

  PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
  clientBuilder.setConnectionManager(connectionManager);
  _client = clientBuilder.build();
}

此时我可以使用客户端执行请求,并且只要将服务器配置为允许访问 clientcert.p12,客户端身份验证就可以正常工作。

我需要的是能够根据所需客户端证书的值动态更改每个请求的客户端证书。

是否可以在动态更改客户端证书的同时重用静态 HttpClient?另外,如果可能的话,我是否还会看到使用池连接管理器的性能优势?

【问题讨论】:

    标签: java apache-httpclient-4.x


    【解决方案1】:

    有一个未记录的http.socket-factory-registry 执行上下文属性可以用来覆盖连接管理器在构建时设置的连接套接字工厂。

    CloseableHttpClient httpClient = HttpClientBuilder.create()
            .setSSLContext(SSLContexts.createSystemDefault())
            .build();
    
    SSLContext customSSlContext = SSLContexts.custom()
            .loadKeyMaterial(new File("my-keystore.jks"), "sectret".toCharArray(),  "sectret".toCharArray())
            .build();
    
    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory())
            .register("https", new SSLConnectionSocketFactory(customSSlContext))
            .build();
    
    HttpClientContext clientContext = HttpClientContext.create();
    clientContext.setAttribute("http.socket-factory-registry", socketFactoryRegistry);
    try (CloseableHttpResponse response = httpClient.execute(new HttpGet("https://host/stuff"), clientContext)) {
        System.out.println(response.getStatusLine());
        EntityUtils.consume(response.getEntity());
    }
    

    在使用相同的客户端实例/相同的连接池来执行具有不同用户身份/安全上下文的多个线程的请求时,请格外小心。

    【讨论】:

    • 不确定为什么这个问题和答案没有得到所有的支持。救了我!在微服务世界中,可以使用多个证书从一个服务调用多个服务,这很高兴知道!
    【解决方案2】:

    我在这里使用PoolingHttpClientConnectionManager 类发布我的解决方案,这个对我有用:

    SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext, 新的 DefaultHostnameVerifier());

        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
                .register("https", sslConnectionFactory)
                .register("http", new PlainConnectionSocketFactory())
                .build();
    
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
    
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLContext(sslContext)
                .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
                .setConnectionManager(cm)
                .build();
    
        cm.setMaxTotal(200);
        cm.setDefaultMaxPerRoute(20);
        final ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
        engine.setFollowRedirects(false);
    
        ResteasyClient client = clientBuilder.httpEngine(engine).build();
        ResteasyWebTarget target = client.target(UriBuilder.fromPath(
                "https://my.server.com/mtls/protected/resource"));
        String response = target.request().get(String.class);
    

    【讨论】:

      猜你喜欢
      • 2022-06-23
      • 1970-01-01
      • 1970-01-01
      • 2018-10-20
      • 2017-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多