ApacheHttp5 FeignClient SSL and Proxy Connection in Spring Boot
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:-
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:-
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:-
- You have
spring-boot-starter-web
,spring-cloud-starter-openfeign
, andio.github.openfeign:feign-hc5
dependencies in your pom.xml or build.gradle - You are using
@SpringBootApplication
and@EnableFeignClients
annotations at your application starter class fileApiApplication
.package com.example.api; @SpringBootApplication @EnableFeignClients public class ApiApplication { public static void main(String[] args) { SpringApplication.run(ApiApplication.class, args); } }
- You set
feign.httpclient.enabled
property totrue
in application.properties or application.yml filefeign.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 -
- Annotated with
@FeignClient
which auto scan by spring boot application to generate feign client - Consumes the APIs from this URL:
https://reqres.in
- Get the ApacheHttp5 SSL and Proxy connection from configuration class
ApacheHttp5FeignSslClientConfig.class
- 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