ApacheHttp5 FeignClient SSL and Proxy Connection in Spring Boot ApacheHttp5 FeignClient SSL and Proxy Connection in Spring Boot

Page content

In this article, we’ll learn how to configure a FeignClient with SSL and Proxy Connection using ApacheHttp5 in your Spring Boot project.

Project Setup

For initial setup of your Spring Boot project, you should use Spring Initializr. Choose the OpenFeign and Spring Web as dependencies and Contract Stub Runner as test dependency.

Maven Project

You can click the below link to generate a Maven project with pre-selected dependencies:-

https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.5.1.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=api&name=api&description=Create%20Feign%20Client%20to%20consume%20RESTFul%20APIs&packageName=com.example.api&dependencies=cloud-feign,web,cloud-contract-stub-runner

and then add io.github.openfeign:feign-hc5 dependency.

<!-- to write web layer -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- to write web client using OpenFeign -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!-- to write test class using junit jupiter -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

<!-- to write integration test and mock stub using WireMock -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
  <scope>test</scope>
</dependency>

<!-- to add ApacheHttp5 openfeign client -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-hc5</artifactId>
</dependency>

Gradle Project

Similarly, You can click the below link to generate a Gradle project with pre-selected dependencies:-

https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.5.1.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=api&name=api&description=Create%20Feign%20Client%20to%20consume%20RESTFul%20APIs&packageName=com.example.api&dependencies=cloud-feign,web,cloud-contract-stub-runner

and then add io.github.openfeign:feign-hc5 dependency.

dependencies {
  // to write web layer
  implementation 'org.springframework.boot:spring-boot-starter-web'

  // to write web client using OpenFeign
  implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

  // to write test class using junit jupiter
  testImplementation 'org.springframework.boot:spring-boot-starter-test'

  // to write integration test and mock stub using WireMock
  testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner'

  // to add ApacheHttp5 openfeign client
  implementation 'io.github.openfeign:feign-hc5'
}

Enable ApacheHttp5 Feign Client

When you are working with spring boot project, you have nothing much to do to enable ApacheHttp5 FeignClient for your project. Make sure:-

  1. You have spring-boot-starter-web, spring-cloud-starter-openfeign, and io.github.openfeign:feign-hc5 dependencies in your pom.xml or build.gradle
  2. You are using @SpringBootApplication and @EnableFeignClients annotations at your application starter class file ApiApplication.
    package com.example.api;
    
    @SpringBootApplication
    @EnableFeignClients
    public class ApiApplication {
    
      public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
      }
    }
    
  3. You set feign.httpclient.enabled property to true in application.properties or application.yml file
    feign.httpclient.enabled: true
    

Create ApacheHttp5 Feign Client with SSL and Proxy Connection

Next, we are going to create a FeignClient to consume secured APIs using ApacheHttp5 SSL and Proxy Connection.

Let’s create a UserFeignClient interface -

  1. Annotated with @FeignClient which auto scan by spring boot application to generate feign client
  2. Consumes the APIs from this URL: https://reqres.in
  3. Get the ApacheHttp5 SSL and Proxy connection from configuration class ApacheHttp5FeignSslClientConfig.class
  4. Get the properties value such as baseUrl, ssl and proxy connection from application.yml file
application.yml
client:
  api:
    baseUrl: https://reqres.in
    ssl:
      protocol: TLS
      key-store-type: JKS
      key-store: classpath:KeyStore.jks
      key-store-password: changeit
      key-password: changeit
      trust-store: classpath:TrustStore.jks
      trust-store-password: changeit
      proxy-host: 10.20.30.40
      proxy-port: 443
Feign Client
@FeignClient(name = "userFeignClient",
    url = "${client.api.baseUrl}",
    configuration = ApacheHttp5FeignSslClientConfig.class)
public interface UserFeignClient {

    @GetMapping("/api/users")
    ListUserResponse getUserList(@RequestParam("page") Integer page);

    @GetMapping("/api/users/{userId}")
    SingleUserResponse getUserById(@PathVariable("userId") Long userId);

    @PostMapping("/api/users")
    User createUser(@RequestBody UserRequest userRequest);

    @PutMapping("/api/users")
    User updateUser(@RequestBody UserRequest userRequest);

    @DeleteMapping("/api/users/{userId}")
    void deleteUserById(@PathVariable("userId") Long userId);
}
ApacheHttp5 Feign Client Configuration
import feign.Feign;
import feign.Retryer;
import feign.hc5.ApacheHttp5Client;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.util.ResourceUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

@Slf4j
public class ApacheHttp5FeignSslClientConfig {

    @Bean
    public Feign.Builder feignBuilder(
        @Value("${client.api.ssl.protocol}") String protocol,
        @Value("${client.api.ssl.key-store-type}") String keyStoreType,
        @Value("${client.api.ssl.key-store}") String keyStore,
        @Value("${client.api.ssl.key-store-password}") String keyStorePassword,
        @Value("${client.api.ssl.key-password}") String keyPassword,
        @Value("${client.api.ssl.trust-store}") String trustStore,
        @Value("${client.api.ssl.trust-store-password}") String trustStorePassword,
        @Value("${client.api.ssl.proxy-host}") String proxyHost,
        @Value("${client.api.ssl.proxy-port}") String proxyPort

    ) {
        SSLContext sslContext = getSSLContext(protocol, keyStoreType, keyStore, keyStorePassword, keyPassword, trustStore, trustStorePassword);
        SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactoryBuilder.create().setSslContext(sslContext).build();
        HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslConnectionSocketFactory).build();
        return Feign.builder()
            .retryer(Retryer.NEVER_RETRY)
            .client(new ApacheHttp5Client(HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setProxy(StringUtils.isNotEmpty(proxyHost) ? new HttpHost(proxyHost, proxyPort) : null)
                .build()));
    }

    private SSLContext getSSLContext(String protocol, String keyStoreType, String keyStore, String keyStorePassword, String keyPassword, String trustStore, String trustStorePassword) {
        try {
            TrustStrategy acceptingTrustStrategy = (chain, authType) -> true;
            return SSLContexts.custom()
                .setProtocol(protocol)
                .setKeyStoreType(keyStoreType)
                .loadKeyMaterial(ResourceUtils.getFile(keyStore), keyStorePassword.toCharArray(), keyPassword.toCharArray())
                .loadTrustMaterial(ResourceUtils.getFile(trustStore), trustStorePassword.toCharArray(), acceptingTrustStrategy)
                .build();
        } catch (IOException | UnrecoverableKeyException | CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            log.error("Error while building SSLContext for ApacheHttp5FeignSslClient", e);
            throw new ExceptionInInitializerError("Error while building SSLContext for ApacheHttp5FeignSslClient");
        }
    }
}

Please note that if you don’t want to set the proxy in the above configuration, then keep the proxy-host and proxy-port values as blank in property file.

Conclusion

We learned how to configure the latest version of Apache Http Client i.e. ApacheHttp5 to use with OpenFeign in Spring Boot Application. We also learned how to load SSL certificates and set Proxy in ApacheHttp5 client.

Download the complete source code for the examples in this post from github/springboot-openfeign