【问题标题】:Spring Webflux - WebClient over TLSv1.2Spring Webflux - 基于 TLSv1.2 的 WebClient
【发布时间】:2021-03-02 20:27:13
【问题描述】:

关于 Spring Webflux WebClient 的小问题,尤其是如何配置一个使用 TLSv1.2 发送出站请求。

我的应用是 Spring Webflux 2.4.2,其中启用了 HTTP/2、SSL、mTLS 和 TLSv1.3。

server.ssl.enabled-protocols=TLSv1.3
server.http2.enabled=true
server.ssl.client-auth=need
server.ssl.enabled=true
server.ssl.key-alias=alias
server.ssl.key-password=
server.ssl.key-store-password=
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=JKS
server.ssl.key-store=/keystore.p12
server.ssl.trust-store-password=
server.ssl.trust-store-provider=SUN
server.ssl.trust-store-type=JKS
server.ssl.trust-store=/truststore.p12

注意它是 TLSv1.3,而不是设置上的 TLSv1,2。

当我创建一个 WebClient 实例时:

getWebClient().mutate().baseUrl("https://some-server.com").build().post().uri("/some/route").retrieve().bodyToMono(String.class);

 @Bean
    @Primary
    public WebClient getWebClient() {
        return WebClient.create().mutate().defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE, HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).clientConnector(new ReactorClientHttpConnector(HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(getSslContextBuilder())))).build();
    }


    public SslContextBuilder getSslContextBuilder() {
        try {
            final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            try (InputStream file = new FileInputStream(keyStorePath)) {
                final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
                keyStore.load(file, keyStorePassPhrase.toCharArray());
                keyManagerFactory.init(keyStore, keyPassPhrase.toCharArray());
            }

            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            try (InputStream trustStoreFile = new FileInputStream(trustStorePath)) {
                final KeyStore trustStore = KeyStore.getInstance(trustStoreType);
                trustStore.load(trustStoreFile, trustStorePassPhrase.toCharArray());
                trustManagerFactory.init(trustStore);
            }

            final ApplicationProtocolConfig applicationProtocolConfig = new ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, ApplicationProtocolNames.HTTP_2, ApplicationProtocolNames.HTTP_1_1);
            return SslContextBuilder.forClient().applicationProtocolConfig(applicationProtocolConfig).keyManager(keyManagerFactory).trustManager(InsecureTrustManagerFactory.INSTANCE);
        } catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException | UnrecoverableKeyException e) {
            return null;
        }
    }

并使用它向启用 TLSv1.3 的服务器发送请求,它工作正常,非常高兴。

但是,我需要与之交互的一些服务器要么具有旧的/不同的 JDK 版本,要么启用了 TLSv1.2(我不拥有那些第三方 API)。

因为我得到:

io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: extension (5) should not be presented in certificate_request

在 SO 中研究后,我发现我可以配置这样的。

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
        SpringApplication.run(MyApplication.class);
    }

令人惊讶的是,问题已解决(不知道为什么)。

我的问题是:我可以简单地在代码级别使用 WebClient 实现相同的功能,而不是设置系统属性吗?

谢谢

【问题讨论】:

  • 有问题的服务器不是 TLS1.2,而是 1.3 使用带有状态 aka stapling 的客户端身份验证(不在 Java 中);这是在 15 14.0.2 13.0.3 11.0.7 8u261 中修复的 Java 中的错误,并且不影响 1.2,这就是强制客户端使用 1.2 可以避免问题的原因。应该可以在你的 SSLContext 中做到这一点,但我不是弹簧人,所以我无法回答那部分。
  • 但这已经是很好的洞察力了,学到了很多。谢谢。希望有人可以顺便来帮助 SSLContext 部分????

标签: java spring


【解决方案1】:

截至今天,System.setProperty("jdk.tls.client.protocols", "TLSv1.2"); 是解决方案

【讨论】:

    猜你喜欢
    • 2018-05-09
    • 2018-08-18
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 2021-09-10
    • 2017-12-31
    • 2021-08-06
    • 2019-12-28
    相关资源
    最近更新 更多