简体   繁体   English

为spring-webflux WebClient配置具有reactor netty的HostnameVerifier

[英]Configure HostnameVerifier with reactor netty for spring-webflux WebClient

I'm trying to configure spring-webflux WebClient (with reactor netty under the hood) with ssl and client hostname verification. 我正在尝试使用ssl和客户端主机名验证配置spring-webflux WebClient(带反应堆netty)。 I'm provided with javax.net.ssl.SSLContext, HostnameVerifier and a list of trusted hostnames (as string list). 我提供了javax.net.ssl.SSLContext,HostnameVerifier和可信主机名列表(作为字符串列表)。

So far I've configured WebClient with my SSLContext, but I can't find a way to configure hostname verification. 到目前为止,我已经使用我的SSLContext配置了WebClient,但是我找不到配置主机名验证的方法。

To state my problem: I have a set of trusted services hostnames (String list) and a HostnameVerifier. 说明我的问题:我有一组受信任的服务主机名(字符串列表)和一个HostnameVerifier。 I want to configure my WebClient with it. 我想用它配置我的WebClient。

Is there a possibility to do it with the javax.net.ssl.HostnameVerifier? 是否有可能使用javax.net.ssl.HostnameVerifier进行此操作? Is there an alternative approach in reactor netty? 反应堆网络中是否有替代方法?

This is what I've got so far: 这是我到目前为止所得到的:

WebClient.builder()
  .clientConnector(
    new ReactorClientHttpConnector(
      opt -> opt.sslContext(new JdkSslContext(mySSLContext, 
                      true, ClientAuth.OPTIONAL))))
  .build();

You should provide a valid certificate authority certificate ( trustManager() ) and optionally user cert with private key and private key password for authorization ( keyManager() ). 您应该提供有效的证书颁发机构证书( trustManager() )以及可选的用户证书,其中包含用于授权的私钥和私钥密码( keyManager() )。 Your service SSL certificate should be signed by the same CA that you've defined in trustManager(). 您的服务SSL证书应由您在trustManager()中定义的CA签名。

Hostnames are being verified automatically with service hostname. 正在使用服务主机名自动验证主机名。 If there's not match java.security.cert.CertificateException: No subject alternative names present exception will be thrown. 如果不匹配java.security.cert.CertificateException: No subject alternative names present异常将被抛出。 Actually I can't find a way to omit hostnames verification (without omitting whole SSL certificate verification by using .trustManager(InsecureTrustManagerFactory.INSTANCE) ). 实际上我找不到省略主机名验证的方法(不使用.trustManager(InsecureTrustManagerFactory.INSTANCE)省略整个SSL证书验证)。

I've tested this solution locally. 我在本地测试了这个解决方案。 My webservice is running on my local machine but its SSL certificate contains only DNS name, not the IP address. 我的webservice在我的本地计算机上运行,​​但其SSL证书仅包含DNS名称,而不包含IP地址。 So for my debug purposes I've added entry to hosts file and mapped my service IP to proper DNS name. 因此,为了我的调试目的,我已经添加了对hosts文件的条目,并将我的服务IP映射到正确的DNS名称。

SslContext sslContext = SslContextBuilder
        .forClient()
        .trustManager(new FileInputStream(caPath))
        .keyManager(
                new FileInputStream(userCertPath),
                new FileInputStream(userPrivateKeyPath),
                userPrivateKeyPassword
        )
        .build();

HttpClient httpClient = HttpClient.create()
        .secure(t -> t.sslContext(sslContext));

WebClient webClient = WebClient.builder()
        .clientConnector(new ReactorClientHttpConnector(httpClient))
        .build();

I've tried following solution with Netty HttpClient and it worked as well (Disabling the hostname verification by using a custom hostname matcher) 我尝试过使用Netty HttpClient的解决方案,它也运行良好(通过使用自定义主机名匹配器禁用主机名验证)

public HttpClient getHttpClient(HttpClientProperties properties){

            // configure pool resources
            HttpClientProperties.Pool pool = properties.getPool();

            ConnectionProvider connectionProvider;
            if (pool.getType() == DISABLED) {
                connectionProvider = ConnectionProvider.newConnection();
            }
            else if (pool.getType() == FIXED) {
                connectionProvider = ConnectionProvider.fixed(pool.getName(),
                        pool.getMaxConnections(), pool.getAcquireTimeout());
            }
            else {
                connectionProvider = ConnectionProvider.elastic(pool.getName());
            }

            HttpClient httpClient = HttpClient.create(connectionProvider)
                    .tcpConfiguration(tcpClient -> {

                        if (properties.getConnectTimeout() != null) {
                            tcpClient = tcpClient.option(
                                    ChannelOption.CONNECT_TIMEOUT_MILLIS,
                                    properties.getConnectTimeout());
                        }

                        // configure proxy if proxy host is set.
                        HttpClientProperties.Proxy proxy = properties.getProxy();

                        if (StringUtils.hasText(proxy.getHost())) {

                            tcpClient = tcpClient.proxy(proxySpec -> {
                                ProxyProvider.Builder builder = proxySpec
                                        .type(ProxyProvider.Proxy.HTTP)
                                        .host(proxy.getHost());

                                PropertyMapper map = PropertyMapper.get();

                                map.from(proxy::getPort).whenNonNull().to(builder::port);
                                map.from(proxy::getUsername).whenHasText()
                                        .to(builder::username);
                                map.from(proxy::getPassword).whenHasText()
                                        .to(password -> builder.password(s -> password));
                                map.from(proxy::getNonProxyHostsPattern).whenHasText()
                                        .to(builder::nonProxyHosts);
                            });
                        }
                        return tcpClient;
                    });

            HttpClientProperties.Ssl ssl = properties.getSsl();
            if (ssl.getTrustedX509CertificatesForTrustManager().length > 0
                    || ssl.isUseInsecureTrustManager()) {
                httpClient = httpClient.secure(sslContextSpec -> {
                    // configure ssl
                    SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();

                    X509Certificate[] trustedX509Certificates = ssl
                            .getTrustedX509CertificatesForTrustManager();
                    if (trustedX509Certificates.length > 0) {
                        sslContextBuilder.trustManager(trustedX509Certificates);
                    }
                    else if (ssl.isUseInsecureTrustManager()) {
                        sslContextBuilder
                                .trustManager(InsecureTrustManagerFactory.INSTANCE);
                    }


                    sslContextSpec.sslContext(sslContextBuilder)
                            .defaultConfiguration(ssl.getDefaultConfigurationType())
                            .handshakeTimeout(ssl.getHandshakeTimeout())
                            .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
                            .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout())
                            .handlerConfigurator(
                                    (handler)->{
                                        SSLEngine engine = handler.engine();
                                        //engine.setNeedClientAuth(true);
                                        SSLParameters params = new SSLParameters();
                                        List<SNIMatcher> matchers = new LinkedList<>();
                                        SNIMatcher matcher = new SNIMatcher(0) {

                                            @Override
                                            public boolean matches(SNIServerName serverName) {
                                                return true;
                                            }
                                        };
                                        matchers.add(matcher);
                                        params.setSNIMatchers(matchers);
                                        engine.setSSLParameters(params);
                                    }
                            )
                    ;
                });
            }

            return httpClient;

        }

It uses nettys handlerConfigurator to configure SSLEngine and use it with custom matchers 它使用nettys handlerConfigurator配置SSLEngine并将其与自定义匹配器一起使用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM