[英]How to configure a Reactive WebClient to use 2-way TLS?
我正在嘗試配置一個反應式 WebClient 以使用 2 路 TLS。 我用這個答案作為參考。 (使用 WebClientCustomizer 的那個,而不是使用 InsecureTrustManager 的那個)。
我仔細檢查了客戶端和服務器端的密鑰庫和信任庫,但服務器發回一個錯誤,指出客戶端沒有提供任何證書:
@Bean
WebClientCustomizer configureWebclient(@Value("${server.ssl.trust-store}") String trustStorePath, @Value("${server.ssl.trust-store-password}") String trustStorePass,
@Value("${server.ssl.key-store}") String keyStorePath, @Value("${server.ssl.key-store-password}") String keyStorePass, @Value("${server.ssl.key-alias}") String keyAlias) {
return new WebClientCustomizer() {
@Override
public void customize(Builder webClientBuilder) {
SslContext sslContext;
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
List<Certificate> certificateCollcetion = Collections.list(trustStore.aliases()).stream().filter(t -> {
try {
return trustStore.isCertificateEntry(t);
} catch (KeyStoreException e1) {
throw new RuntimeException("Error reading truststore", e1);
}
}).map(t -> {
try {
return trustStore.getCertificate(t);
} catch (KeyStoreException e2) {
throw new RuntimeException("Error reading truststore", e2);
}
}).collect(Collectors.toList());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());
sslContext = SslContextBuilder.forClient()
.keyManager((PrivateKey) keyStore.getKey(keyAlias, keyStorePass.toCharArray()))
.trustManager((X509Certificate[]) certificateCollcetion.toArray(new X509Certificate[certificateCollcetion.size()]))
.build();
} catch (Exception e) {
log.error("Error creating web client", e);
throw new RuntimeException(e);
}
ClientHttpConnector connector = new ReactorClientHttpConnector((opt) -> {
opt.sslContext(sslContext);
});
webClientBuilder.clientConnector(connector);
}
};
}
有人可以分享有關如何正確配置反應式 WebClient 以使用 2-way TLS 的見解嗎?
出於某種原因,當 ssl 上下文像這樣構建時,服務器不會接受客戶端證書:
sslContext = SslContextBuilder.forClient()
.keyManager((PrivateKey) keyStore.getKey(keyAlias, keyStorePass.toCharArray()))
.trustManager((X509Certificate[]) certificateCollcetion.toArray(new X509Certificate[certificateCollcetion.size()]))
.build();
為了解決這個問題,我必須初始化一個 KeyManagerFactory:
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, keyStorePass.toCharArray());
然后我用工廠初始化了 ssl 上下文:
SslContext sslContext = SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager((X509Certificate[]) certificateCollection.toArray(new X509Certificate[certificateCollection.size()]))
.build();
之后,服務器接受了證書,我就可以連接了。
總而言之,我使用了這個更簡潔的解決方案,該解決方案將工廠用於密鑰存儲和信任存儲:
@Value("${server.ssl.trust-store}")
String trustStorePath;
@Value("${server.ssl.trust-store-password}")
String trustStorePass;
@Value("${server.ssl.key-store}")
String keyStorePath;
@Value("${server.ssl.key-store-password}")
String keyStorePass;
@Bean
public WebClient create2WayTLSWebClient() {
ClientHttpConnector connector = new ReactorClientHttpConnector(
options -> {
options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
options.sslContext(get2WaySSLContext());
}
);
return WebClient.builder()
.clientConnector(connector)
.build();
}
private SslContext get2WaySSLContext() {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, keyStorePass.toCharArray());
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(trustStore);
return SslContextBuilder.forClient()
.keyManager(keyManagerFactory)
.trustManager(trustManagerFactory)
.build();
} catch (Exception e) {
logger.error("Error creating 2-Way TLS WebClient. Check key-store and trust-store.");
e.printStackTrace();
}
return null;
}
請注意,如果您使用的是 Spring 5.1 或更高版本,則此特定實現將不起作用,因為您無法再將 HttpClientOptions 傳遞給 ReactorClientHttpConnector。 使用此鏈接作為該配置的指南。 然而,這個答案中的代碼內容應該仍然適用於那種配置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.