使用 Apache HttpComponents 使用 Webflux Webclient 配置 SSL
Posted
技术标签:
【中文标题】使用 Apache HttpComponents 使用 Webflux Webclient 配置 SSL【英文标题】:Configure SSL with Webflux Webclient using Apache HttpComponents 【发布时间】:2021-12-18 22:36:53 【问题描述】:我正在尝试从 restTemplate 迁移到 webClient。
在我使用 ClientHttpRequestFactory
到达 restTemplate 配置之前一切都很好。
我在这里粘贴旧代码和新代码。
------带有restTemplate的旧代码-------
private HttpComponentsClientHttpRequestFactory buildRequestFactory()
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPassword));
clientBuilder.useSystemProperties();
clientBuilder.setProxy(proxy);
clientBuilder.setDefaultCredentialsProvider(credsProvider);
clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
TrustStrategy acceptingTrustStrategy = new TrustStrategy()
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
return true;
;
SSLContext sslContext = null;
try
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e)
throw new ServiceException(GlobalErrorMessage.INTERNAL_SERVER_ERROR);
SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = clientBuilder
.setSSLSocketFactory(connectionFactory)
.setRoutePlanner(new DefaultProxyRoutePlanner(proxy)
@Override
public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context)
throws HttpException
if (target.getHostName().equals(noproxy))
return null;
return super.determineProxy(target, request, context);
)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
@Bean(name = "gatewayRestTemplate")
public RestTemplate gatewayRestTemplateConfig()
RestTemplate restTemplate = new RestTemplate(converters());
restTemplate.setRequestFactory(buildRequestFactory());
return restTemplate;
-----使用 webClient 的新代码------
private ClientHttpConnector buildClientConnector()
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
org.apache.hc.core5.http.HttpHost proxy = new org.apache.hc.core5.http.HttpHost(proxyHost, proxyPort);
org.apache.hc.client5.http.auth.CredentialsProvider credsProvider = new org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider();
((org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider) credsProvider).setCredentials(new org.apache.hc.client5.http.auth.AuthScope(proxyHost, proxyPort),
new org.apache.hc.client5.http.auth.UsernamePasswordCredentials(proxyUser, proxyPassword.toCharArray()));
clientBuilder.useSystemProperties();
clientBuilder.setProxy(proxy);
clientBuilder.setDefaultCredentialsProvider(credsProvider);
clientBuilder.setProxyAuthenticationStrategy(new DefaultAuthenticationStrategy());
TrustStrategy acceptingTrustStrategy = new TrustStrategy()
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
return true;
;
SSLContext sslContext = null;
try
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e)
throw new ServiceException(GlobalErrorMessage.INTERNAL_SERVER_ERROR);
org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory connectionFactory =
new org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
org.apache.hc.core5.http.config.Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
// .<org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory>create().register("https", connectionFactory)
.<ConnectionSocketFactory>create().register("https", connectionFactory)
// .register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpAsyncClient client = clientBuilder
.setConnectionManager((AsyncClientConnectionManager) connectionManager)
.setRoutePlanner(new org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner(proxy)
@Override
protected org.apache.hc.core5.http.HttpHost determineProxy(org.apache.hc.core5.http.HttpHost target, org.apache.hc.core5.http.protocol.HttpContext context) throws org.apache.hc.core5.http.HttpException
if (target.getHostName().equals(noproxy))
return null;
return super.determineProxy(target, context);
)
.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
return connector;
@Primary
@Bean(name = "defaultWebClient")
public WebClient defaultWebClientConfig()
WebClient webClient = WebClient.builder()
.clientConnector(buildClientConnector())
.build();
return webClient;
当我运行项目时,我得到了这个异常:
Caused by: java.lang.ClassCastException: class org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager cannot be cast to class org.apache.hc.client5.http.nio.AsyncClientConnectionManager (org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager and org.apache.hc.client5.http.nio.AsyncClientConnectionManager are in unnamed module of loader 'app')
【问题讨论】:
【参考方案1】:基于Migration to Apache HttpClient 5.0 async APIs,我解决了我的问题。这个想法是在设置sslContext
时使用ClientTlsStrategyBuilder
。
private ClientHttpConnector buildClientConnector()
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
org.apache.hc.core5.http.HttpHost proxy = new org.apache.hc.core5.http.HttpHost(proxyHost, proxyPort);
org.apache.hc.client5.http.auth.CredentialsProvider credsProvider = new org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider();
((org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider) credsProvider).setCredentials(new org.apache.hc.client5.http.auth.AuthScope(proxyHost, proxyPort),
new org.apache.hc.client5.http.auth.UsernamePasswordCredentials(proxyUser, proxyPassword.toCharArray()));
clientBuilder.useSystemProperties();
clientBuilder.setProxy(proxy);
clientBuilder.setDefaultCredentialsProvider(credsProvider);
clientBuilder.setProxyAuthenticationStrategy(new DefaultAuthenticationStrategy());
TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
SSLContext sslContext;
try
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e)
throw new ServiceException(GlobalErrorMessage.INTERNAL_SERVER_ERROR);
PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
.setSslContext(sslContext)
.setHostnameVerifier(new NoopHostnameVerifier())
.build())
.build();
CloseableHttpAsyncClient client = clientBuilder
.setConnectionManager(connectionManager)
.setRoutePlanner(new org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner(proxy)
@Override
protected org.apache.hc.core5.http.HttpHost determineProxy(org.apache.hc.core5.http.HttpHost target, org.apache.hc.core5.http.protocol.HttpContext context) throws org.apache.hc.core5.http.HttpException
if (target.getHostName().equals(noproxy))
return null;
return super.determineProxy(target, context);
)
.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
return connector;
@Primary
@Bean(name = "defaultWebClient")
public WebClient defaultWebClientConfig()
WebClient webClient = WebClient.builder()
.clientConnector(buildClientConnector())
.build();
return webClient;
【讨论】:
以上是关于使用 Apache HttpComponents 使用 Webflux Webclient 配置 SSL的主要内容,如果未能解决你的问题,请参考以下文章
Android SDK ~ Cloud Vision:警告:org.apache.httpcomponents:httpclient:4.0.1
Android Gradle 项目中包括 Apache HttpComponents 的问题
Apache HttpComponents 工具类 [ HttpUtil ]