猿问

如何使用不同的 SSL 客户端身份验证证书创建 Apache HttpClients 池

我一直在浏览 Apache 文档和其他示例,试图创建一个使用 Apache HttpClient 来调用各种 RESTful Web 服务的客户端。(这些 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?另外,如果这是可能的,我是否仍然会看到使用池连接管理器的性能优势?


月关宝盒
浏览 183回答 2
2回答

蛊毒传说

可以使用一个未记录的http.socket-factory-registry执行上下文属性来覆盖连接管理器在构造时设置的连接套接字工厂。CloseableHttpClient httpClient = HttpClientBuilder.create()&nbsp; &nbsp; &nbsp; &nbsp; .setSSLContext(SSLContexts.createSystemDefault())&nbsp; &nbsp; &nbsp; &nbsp; .build();SSLContext customSSlContext = SSLContexts.custom()&nbsp; &nbsp; &nbsp; &nbsp; .loadKeyMaterial(new File("my-keystore.jks"), "sectret".toCharArray(),&nbsp; "sectret".toCharArray())&nbsp; &nbsp; &nbsp; &nbsp; .build();Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()&nbsp; &nbsp; &nbsp; &nbsp; .register("http", PlainConnectionSocketFactory.getSocketFactory())&nbsp; &nbsp; &nbsp; &nbsp; .register("https", new SSLConnectionSocketFactory(customSSlContext))&nbsp; &nbsp; &nbsp; &nbsp; .build();HttpClientContext clientContext = HttpClientContext.create();clientContext.setAttribute("http.socket-factory-registry", socketFactoryRegistry);try (CloseableHttpResponse response = httpClient.execute(new HttpGet("https://host/stuff"), clientContext)) {&nbsp; &nbsp; System.out.println(response.getStatusLine());&nbsp; &nbsp; EntityUtils.consume(response.getEntity());}使用相同的客户端实例/相同的连接池来执行具有不同用户身份/安全上下文的多个线程的请求时要格外小心。

慕娘9325324

我在这里发布我使用PoolingHttpClientConnectionManager类的解决方案,这个对我有用:SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier());&nbsp; &nbsp; Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .register("https", sslConnectionFactory)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .register("http", new PlainConnectionSocketFactory())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build();&nbsp; &nbsp; PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);&nbsp; &nbsp; CloseableHttpClient httpClient = HttpClients.custom()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setSSLContext(sslContext)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setConnectionManager(cm)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build();&nbsp; &nbsp; cm.setMaxTotal(200);&nbsp; &nbsp; cm.setDefaultMaxPerRoute(20);&nbsp; &nbsp; final ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);&nbsp; &nbsp; engine.setFollowRedirects(false);&nbsp; &nbsp; ResteasyClient client = clientBuilder.httpEngine(engine).build();&nbsp; &nbsp; ResteasyWebTarget target = client.target(UriBuilder.fromPath(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "https://my.server.com/mtls/protected/resource"));&nbsp; &nbsp; String response = target.request().get(String.class);
随时随地看视频慕课网APP

相关分类

Java
我要回答