web-dev-qa-db-fra.com

Spring 5 WebClient utilisant ssl

J'essaie de trouver des exemples d'utilisation de WebClient. Mon objectif est d'utiliser Spring 5 WebClient pour interroger un service REST en utilisant https et un certificat auto-signé

Un exemple?

11
Seb

Voir exemple d'utilisation TrustManagerFactory non sécurisé qui fait confiance à tous les certificats X.509 (y compris auto-signés) sans aucune vérification. La note importante de la documentation:

N'utilisez jamais ce TrustManagerFactory en production. C'est uniquement à des fins de test, et donc c'est très peu sûr.

@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();
}
14
Venelin

Il semble que Spring 5.1.1 (Spring boot 2.1.0) ait supprimé HttpClientOptions de ReactorClientHttpConnector, vous ne pouvez donc pas configurer d'options lors de la création d'une instance de ReactorClientHttpConnector

Une option qui fonctionne maintenant est:

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()

Fondamentalement, lors de la création du HttpClient, nous configurons le non sécurisé sslContext, puis nous transmettons ce httpClient pour l'utiliser dans ReactorClientHttpConnector globalement.

L'autre option consiste à configurer TcpClient avec sslContext non sécurisé et à l'utiliser pour créer une instance de HttpClient, comme illustré ci-dessous:

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()

Pour plus d'informations:

10
Munish Chandel

J'ai dû éditer ceci, pour s'adapter aux changements Spring-boot 2.0-> 2.1.

Si vous souhaitez programmer le code de production, vous pouvez également créer un bean Spring comme celui-ci, qui modifie le client Web injecté, en utilisant les paramètres du serveur Spring-Boot pour savoir où se trouvent le magasin de clés de confiance et le magasin de clés. Dans le client, vous n'avez besoin de donner le magasin de clés que si vous utilisez SSL bidirectionnel. Je ne sais pas pourquoi le ssl-stuff n'est pas préconfiguré et facilement injectable, similaire aux paramètres du serveur Spring-Boot vraiment cool.

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);
          }
        };
  }

Voici la partie, où vous utilisez le 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();
  }
}
6
Frischling