Mastering Secure HTTPS Calls in Spring: Effortless Proxy Configuration with Basic Authentication

Bogdan Covrig
Julius Baer Engineering
3 min readMay 16, 2024

In the realm of secure communication over the internet, making HTTPS endpoint calls via proxies with Basic Authentication is a common requirement for many applications. In this guide, we’ll explore how to streamline this process using Spring and Apache HttpClient.

Prerequisites: Before we delve into the implementation, ensure you have the necessary dependencies included in your project. If you’re using Spring versions 3.x and above, you’ll need to import the Apache HttpClient library 5.x otherwise 4 will also work. Here’s the Maven dependency snippet:

<dependency> 
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>

Let’s start by configuring our proxy settings and establishing the necessary beans in our Spring application. We’ll create a ProxyConfig class where we define our proxy and service host settings, along with SSL truststore configurations.

@Configuration
public class ProxyConfig {


@Value("${https.proxy.host}")
String proxyHostname;

@Value("${https.proxy.port}")
int proxyPort;

@Value("${https.client.host}")
String serviceHost;

@Value("${truststore-location}")
File trustStoreLocation;

@Value("${truststore-password}")
private String truststorePassword;

@Bean
@Qualifier("proxyRestTemplate")
public RestTemplate proxyRestTemplate(RestTemplateBuilder builder) {
return builder
.additionalCustomizers(new ProxyCustomizer(proxyHostname, proxyPort, ServiceHost, trustStoreLocation, truststorePassword)).build();
}
}

Next, we’ll implement the ProxyCustomizer class, which customises our RestTemplate instance with proxy settings and SSL context. This class handles the setup of the HttpClient with the appropriate proxy and SSL configurations. RoutePlanner is optional based on how you choose to configure the logic. If you use two different beans for RestTemplate then you do not need the RoutePlanner. If you use only one bean, then a route Planner customisation is needed in order to determine which calls are made through the proxy.

class ProxyCustomizer implements RestTemplateCustomizer {

private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ProxyCustomizer.class);

String proxyHostname;
int proxyPort;
String ServiceHost;
File trustStore;
String trustStorePassword;

public ProxyCustomizer(String proxyHostname, int proxyPort, String serviceHost, File trustStore, String trustStorePassword) {
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.serviceHost = ServiceHost;
this.trustStore = trustStore;
this.trustStorePassword = trustStorePassword;
}

@Override
@SneakyThrows
public void customize(RestTemplate restTemplate) {
HttpHost proxy = new HttpHost(proxyHostname, proxyPort);

SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(trustStore, trustStorePassword.toCharArray()).build();
SSLConnectionSocketFactory sslConFactory = new SSLConnectionSocketFactory(sslContext);
HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslConFactory)
.build();

HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(cm)
.setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
@Override
public HttpHost determineProxy(HttpHost target, HttpContext context)
throws HttpException {
if (target.getHostName().equals(serviceHost)) {
LOGGER.info("Service was called through proxy ");
return super.determineProxy(target, context);
}
return null;
}
})
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}

}

Now, we’ll create a ServiceClient class responsible for making HTTPS endpoint calls. We inject the necessary dependencies such as service host, API endpoint, user credentials, and the configured proxyRestTemplate. Here, we demonstrate the usage of BasicAuthenticationInterceptor for adding Basic Auth to requests, along with error handling mechanisms.

@Service
@AllArgsConstructor
public class ServiceClient {

private static final Logger LOGGER = LogManager.getLogger(ServiceClient.class);


@Value("${https.service.host}")
String serviceHost;

@Value("${https.service.api}")
String serviceApi;

@Value("${https.service.user}")
String serviceUser;

@Value("${https.service.password}")
String servicePassword;

RestTemplate proxyRestTemplate;


public Reader get() {

try {
proxyRestTemplate.getInterceptors().add(
new BasicAuthenticationInterceptor(serviceUser, servicePassword));
LOGGER.info("Service {} is called", serviceHost);
String stringResponse = requireNonNull(proxyRestTemplate.execute(
serviceApi,
HttpMethod.GET,
null,
response -> new String(response.getBody().readAllBytes(), StandardCharsets.UTF_8)));

return new StringReader(stringResponse);

} catch (HttpStatusCodeException e) {
...
} catch (RestClientException e) {
...
}
}
}

By following this guide, you’ve learned how to simplify HTTPS endpoint calls with Basic Authentication and proxy configuration using Spring and Apache HttpClient. This approach enhances the security and reliability of your application’s communication over the internet. Feel free to adapt and extend the provided code to suit your specific project requirements.

--

--