hum*_*der 5 ssl spring-boot spring-webflux spring-webclient
我有 2 个服务 A 和 B,它们应该通过 HTTPS 相互通信。我已使用 Spring boot 的 server.ssl.* 属性为这两个应用程序启用了 TLS。我使用 WebClient 进行通信,其中服务 A 将使用 Web 客户端调用 B,而 B 将向 A 发送响应。对于通过 TLS 进行的通信,我的理解是 Web 客户端需要信任库数据,该数据将包含服务的证书被称为服务 B。
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import org.springframework.boot.web.server.Ssl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.extern.slf4j.Slf4j;
import reactor.netty.http.client.HttpClient;
@Slf4j
@Configuration
public class WebClientConfig {
private final Ssl ssl;
public WebClientConfig(final Ssl ssl) {
this.ssl = ssl;
}
@Bean
public WebClient createWebClient() throws Exception {
if (ssl.isEnabled()) {
return buildSslEnabledWebClient();
}
return WebClient.builder().build();
}
private WebClient buildSslEnabledWebClient() throws Exception {
final String trustStorePath = ssl.getTrustStore();
final String trustStorePassword = ssl.getTrustStorePassword();
final KeyStore trustStore = createKeyStore(trustStorePath, trustStorePassword);
try {
final TrustManagerFactory trustManager = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManager.init(trustStore);
final SslContext sslContext = SslContextBuilder.forClient().trustManager(trustManager)
.build();
final HttpClient httpClient = HttpClient.create().secure(ssl -> {
ssl.sslContext(sslContext);
});
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();
} catch (NoSuchAlgorithmException | KeyStoreException | SSLException e) {
log.error("Could not initialize Webclient with the trustore data", e);
throw e;
}
}
private static KeyStore createKeyStore(final String keyStoreLocation, final String keyStorePassword) {
try (FileInputStream fis = new FileInputStream(keyStoreLocation)) {
final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(fis, keyStorePassword.toCharArray());
return ks;
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
throw new IllegalArgumentException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经使用 keytool 创建了自签名证书,这似乎有效。我可以从 A 向 B 拨打电话并得到回复。
然而,我不知何故觉得设置不完整,因此我的问题是:
更新:修复了代码,在阅读下面的答案后意识到错误。我实际上正在使用更新后的代码。
在我看来没问题,您正在正确加载密钥库文件,并且还使用密钥库对象正确初始化 trustmanagerfactory。
但是,您没有使用 trustmanagerfactory 来配置 httpclient。相反,您再次从文件加载密钥库,同时将其作为对象传递给方法SslContextBuilder.forClient().trustManager。但是,这不起作用,因为在传递密钥库时,此方法需要一个包含 PEM 格式的受信任证书列表的文件。javadoc 包含以下文档:
public SslContextBuilder trustManager(java.io.File trustCertCollectionFile)
Trusted certificates for verifying the remote endpoint's certificate. The file should contain an X.509 certificate collection in PEM format. null uses the system default.
Run Code Online (Sandbox Code Playgroud)
如果您正在加载 PEM 格式的文件,那么您发布的示例将有效。如果您正在加载密钥库文件,我建议您使用以下代码片段:
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
final SslContext sslContext = SslContextBuilder.forClient()
.trustManager(trustManagerFactory)
.build();
final HttpClient httpClient = HttpClient.create()
.secure(ssl -> {
ssl.sslContext(sslContext);
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10544 次 |
| 最近记录: |