Spring 5 WebClient using ssl

后端 未结 3 2027
北海茫月
北海茫月 2020-12-01 02:17

I\'m trying to find examples of WebClient use.

My goal is to use Spring 5 WebClient to query a REST service using https and self signed certificate

Any example

相关标签:
3条回答
  • 2020-12-01 02:40

    Looks like Spring 5.1.1 (Spring boot 2.1.0) removed HttpClientOptions from ReactorClientHttpConnector, so you can not configure options while creating instance of ReactorClientHttpConnector

    One option that works now is:

    val sslContext = SslContextBuilder
                .forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE)
                .build()
    val httpClient = HttpClient.create().secure { t -> t.sslContext(sslContext) }
    val webClient = WebClient.builder().clientConnector(ReactorClientHttpConnector(httpClient)).build()
    

    Basically while creating the HttpClient, we are configuring the insecure sslContext, and then passing this httpClient for use in ReactorClientHttpConnector globally.

    The other option is to configure TcpClient with insecure sslContext and use it to create HttpClient instance, as illustrated below:

    val sslContext = SslContextBuilder
                .forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE)
                .build()
    val tcpClient = TcpClient.create().secure { sslProviderBuilder -> sslProviderBuilder.sslContext(sslContext) }
    val httpClient = HttpClient.from(tcpClient)
    val webClient =  WebClient.builder().clientConnector(ReactorClientHttpConnector(httpClient)).build()
    

    For more information:

    • https://docs.spring.io/spring/docs/5.1.1.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-builder-reactor
    • https://netty.io/4.0/api/io/netty/handler/ssl/util/InsecureTrustManagerFactory.html

    Update: Java version of the same code

    SslContext context = SslContextBuilder.forClient()
        .trustManager(InsecureTrustManagerFactory.INSTANCE)
        .build();
                    
    HttpClient httpClient = HttpClient.create().secure(t -> t.sslContext(context));
    
    WebClient wc = WebClient
                        .builder()
                        .clientConnector(new ReactorClientHttpConnector(httpClient)).build();
    
    0 讨论(0)
  • 2020-12-01 02:59

    See example of use insecure TrustManagerFactory that trusts all X.509 certificates (including self-signed) without any verification. The important note from documentation:

    Never use this TrustManagerFactory in production. It is purely for testing purposes, and thus it is very insecure.

    @Bean
    public WebClient createWebClient() throws SSLException {
        SslContext sslContext = SslContextBuilder
                .forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE)
                .build();
        ClientHttpConnector httpConnector = HttpClient.create().secure(t -> t.sslContext(sslContext) )
        return WebClient.builder().clientConnector(httpConnector).build();
    }
    
    0 讨论(0)
  • 2020-12-01 03:00

    Had to edit this, to accomodate spring-boot 2.0->2.1 changes.

    Another way, if you want to program production code is, to create a spring bean like such, that modifies the injected webclient, using the settings from the spring-boot server for where the truststore and keystore are. In the client, you only need to give the keystore, if you are using 2-way-ssl. Not sure, why the ssl-stuff is not preconfigured and easily injectable, similar to the really cool spring-boot server settings.

    import io.netty.handler.ssl.SslContext;
    import io.netty.handler.ssl.SslContextBuilder;
    .
    .
    .
    
      @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 (WebClient.Builder webClientBuilder) -> {
              SslContext sslContext;
              final PrivateKey privateKey;
              final X509Certificate[] certificates;
              try {
                final KeyStore trustStore;
                final KeyStore keyStore;
                trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
                keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());
                List<Certificate> certificateList = 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());
                certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
                privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyStorePass.toCharArray());
                Certificate[] certChain = keyStore.getCertificateChain(keyAlias);
                X509Certificate[] x509CertificateChain = Arrays.stream(certChain)
                    .map(certificate -> (X509Certificate) certificate)
                    .collect(Collectors.toList())
                    .toArray(new X509Certificate[certChain.length]);
                sslContext = SslContextBuilder.forClient()
                    .keyManager(privateKey, keyStorePass, x509CertificateChain)
                    .trustManager(certificates)
                    .build();
    
                HttpClient httpClient = HttpClient.create()
                    .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
                ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
                webClientBuilder.clientConnector(connector);
              } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException | UnrecoverableKeyException e) {
                throw new RuntimeException(e);
              }
            };
      }
    

    Here the part, where you use the Webclient: import org.springframework.web.reactive.function.client.WebClient;

    @Component
    public class ClientComponent {
    
      public ClientComponent(WebClient.Builder webClientBuilder, @Value("${url}") String url) {
        this.client = webClientBuilder.baseUrl(solrUrl).build();
      }
    }
    
    0 讨论(0)
提交回复
热议问题