Examples

Use Case 1

The WebClientConfig class demonstrates how to configure a WebClient bean in a Spring application. It supports the following features:

  • Skipping SSL verification for development or testing purposes.

  • Using a custom SSL certificate for secure communication.

  • Configuring an HTTP proxy for outgoing requests.

  • The custom SSL certificate should be in PEM format (Privacy-Enhanced Mail), which is commonly used for X.509 certificates.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath />
    </parent>

    <groupId>com.company.project</groupId>
    <artifactId>sample-springboot-service</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <java.version>21</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring WebFlux for WebClient -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <!-- Reactor Netty for HttpClient, SSL, and proxy support -->
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-http</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.36</version>
        </dependency>
    </dependencies>
</project>

WebClientConfig.java

package com.company.project.config;

import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.File;
import javax.net.ssl.SSLException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;

@Slf4j
@Configuration
public class WebClientConfig {

    @Value("${webclient.base-url}")
    private String baseUrl;

    @Value("${webclient.skip-ssl-verification-and-proxy:false}")
    private boolean skipSslVerification;

    @Value("${webclient.proxy-ip:}")
    private String proxyIP;

    @Value("${webclient.proxy-port:0}")
    private int proxyPort;

    @Value("${webclient.ssl-cert-path:}")
    private String sslCertPath;

    /**
     * Configures and provides a WebClient bean with optional SSL verification skipping and proxy support.
     */
    @Bean
    public WebClient webClient(WebClient.Builder webClientBuilder) {
        HttpClient httpClient = HttpClient.create();

        log.info("WebClient configuration: skipSslVerification={}, sslCertPath={}, proxyIP={}, proxyPort={}",
            skipSslVerification, sslCertPath, proxyIP, proxyPort);

        // SSL configuration
        if (skipSslVerification) {
            httpClient = configureSkipSsl(httpClient);
        } else if (sslCertPath != null && !sslCertPath.isEmpty()) {
            httpClient = configureCustomSsl(httpClient, sslCertPath);
        }

        // Proxy configuration
        if (proxyIP != null && !proxyIP.isEmpty() && proxyPort > 0) {
            httpClient = configureProxy(httpClient);
        }

        return webClientBuilder
            .baseUrl(baseUrl)
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .build();
    }

    private HttpClient configureSkipSsl(HttpClient httpClient) {
        log.debug("Configuring HttpClient to skip SSL verification");
        return httpClient.secure(sslContextSpec -> {
            try {
                sslContextSpec.sslContext(SslContextBuilder
                    .forClient()
                    .trustManager(InsecureTrustManagerFactory.INSTANCE)
                    .build());
            } catch (SSLException e) {
                throw new IllegalStateException("Failed to configure SSL context for skipping verification", e);
            }
        });
    }

    private HttpClient configureCustomSsl(HttpClient httpClient, String certPath) {
        log.info("Configuring HttpClient with custom SSL certificate: {}", certPath);
        return httpClient.secure(sslContextSpec -> {
            try {
                sslContextSpec.sslContext(SslContextBuilder
                    .forClient()
                    .trustManager(new File(certPath))
                    .build());
            } catch (SSLException e) {
                throw new IllegalStateException("Failed to configure SSL context with custom certificate", e);
            }
        });
    }

    private HttpClient configureProxy(HttpClient httpClient) {
        if (proxyIP != null && !proxyIP.isEmpty() && proxyPort > 0) {
            log.info("Configuring HttpClient proxy: {}:{}", proxyIP, proxyPort);
            return httpClient.proxy(proxy -> proxy
                .type(ProxyProvider.Proxy.HTTP)
                .host(proxyIP)
                .port(proxyPort));
        }
        return httpClient;
    }
}

application.yaml

webclient:
  base-url=: https://our-api-url
  skip-ssl-verification-and-proxy: false
  proxy-ip:
  proxy-port: 0
  ssl-cert-path: /etc/ssl/certs/my-custom-cert.pem

We can call the endpoint like below sample

@RequiredArgsConstructor
@Component
public class SampleClient {

    private final WebClient webClient;

    public ResponseDto callApi(RequestDto payload) {
        return webClient.post()
            .uri("/api/some/endpoint")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(payload)
            .retrieve()
            .bodyToMono(ResponseDto.class)
            .block();
    }
}

Last updated