AdBlock Detected

It looks like you're using an ad-blocker!

Our team work realy hard to produce quality content on this website and we noticed you have ad-blocking enabled. Advertisements and advertising enable us to continue working and provide high-quality content.

SSL with WebClient in Spring Boot

Next, we will show you how to configure SSL with WebClient in Spring and how to solve one of the most common problems that may arise when configuring a WebClient, which is how to set it up for an SSL connection.

If you have no experience with WebClient, we recommend reading our previous article on how to use and implement it. Click here to go there.

Why use SSL with WebClient in Spring?

Using SSL (Secure Sockets Layer) with WebClient in Spring provides several benefits:

  1. Secure Communication: SSL encrypts the data transmitted between the client and server, ensuring confidentiality and preventing unauthorized access or eavesdropping. It adds a layer of security to protect sensitive information.
  2. Data Integrity: SSL includes mechanisms to verify the integrity of data, ensuring that it remains intact and unaltered during transmission. This protects against data tampering or modification by malicious actors.
  3. Authentication: SSL enables client and server authentication, verifying the identities of both parties involved in the communication. This helps prevent man-in-the-middle attacks and ensures that the client is connecting to the intended server.
  4. Compliance: Many industries and regulatory frameworks require the use of SSL for secure communication, especially when handling sensitive data such as personal information, financial details, or healthcare records. Using SSL with WebClient helps meet these compliance requirements.
  5. Trust and Reputation: By implementing SSL, you enhance the trustworthiness and reputation of your application or service. It reassures users that their data is being transmitted securely, increasing their confidence in your platform.

Overall, using SSL with WebClient in Spring ensures secure and reliable communication, protects sensitive information, and helps meet security and compliance standards.

Libraries required to add SSL in WebClient

Before we begin, we need to add the following libraries to our pom.xml, so let’s include them:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.projectreactor</groupId>
    <artifactId>reactor-spring</artifactId>
    <version>${reactor-version}</version>
</dependency>
        <!--netty-->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-reactor-netty</artifactId>
 </dependency>
<dependency>
     <groupId>io.projectreactor.ipc</groupId>
     <artifactId>reactor-netty</artifactId>
     <version>0.7.15.RELEASE</version>
</dependency>

We will be using different SSL classes from Netty, which is a framework that allows us to build asynchronous, event-driven applications.

Let’s get started

Now, we will demonstrate two different implementations. The first one will be a configuration that trusts all certificates, and the second one will involve using a specific certificate.

Implementation: WebClient trusting all X.509 certificates (InsecureTrustManagerFactory)

@Bean
public WebClient webClient(String baseUrl) throws Exception {
    var sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();

    var httpConnector = new ReactorClientHttpConnector(options -> {
      options.sslContext(sslContext);
      options.option(ChannelOption.SO_TIMEOUT, timeout);
    });

    return WebClient.builder().clientConnector(httpConnector)
                    .defaultHeader(HttpHeaders.CONTENT_TYPE, 
                     MediaType.APPLICATION_JSON_VALUE)
                     .baseUrl(baseUrl).build();
  }

To add an SSL context, we will follow these three steps:

  1. Create the SslContext object.
  2. Define an httpConnector.
  3. Create a WebClient with the defined connector.

Implementation: WebClient with a specific certificate

It’s important to note that the previously proposed solution is not recommended for production use. Here, we will show you how to implement it with security:

 @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(trustore);
                  } catch (KeyStoreException ex) {
                    throw new RuntimeException("Error getting truststore", e1);
                  }
                })
                .map(trustore -> {
                  try {
                    return trustStore.getCertificate(trustore);
                  } catch (KeyStoreException ex) {
                    throw new RuntimeException("Error getting truststore", ex);
                  }
                })
                .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);
          }
        };
  }

In this code snippet, we perform the following steps:

  1. Read the configuration for the truststore and keystore.
  2. Define the client with the SSL context.
  3. Create the WebClient with the defined HttpClient.

Conclusion:

In this article, we have seen how to configure SSL with WebClient in Spring, which was introduced in Spring version 5 as a replacement for RestTemplate. Due to WebClient’s reactive nature, we had to introduce a reactive library like Netty for SSL configuration.

Implementing security to use this client becomes quite accessible thanks to the use of Spring and the classes provided by Netty.

If you need more information, you can leave us a comment or send an email to refactorizando.web@gmail.com You can also contact us through our social media channels on Facebook or twitter and we will be happy to assist you!!

Leave a Reply

Your email address will not be published. Required fields are marked *