Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relaxed SSL validation with RestTemplate interceptors #1869

Merged
merged 3 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.cloud.contract.wiremock;

import java.lang.reflect.Field;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
Expand All @@ -29,13 +30,20 @@
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInitializer;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.InterceptingClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.util.ReflectionUtils;

import static org.apache.hc.client5.http.ssl.NoopHostnameVerifier.INSTANCE;

/**
* @author Dave Syer
* @author Nikola Kološnjaji
*
*/
@Configuration(proxyBeanMethods = false)
Expand All @@ -48,11 +56,20 @@ public RestTemplateCustomizer wiremockRestTemplateCustomizer() {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory) {
HttpComponentsClientHttpRequestFactory factory = (HttpComponentsClientHttpRequestFactory) restTemplate
.getRequestFactory();
if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory factory) {
factory.setHttpClient(createSslHttpClient());
}
else if (restTemplate.getRequestFactory() instanceof InterceptingClientHttpRequestFactory) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any other way rather than doing this via reflection? Maybe we should ask the Spring team for relaxing some constraints there if there's no other way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to find better way. I will create ticket in Spring framework

Field requestFactoryField = ReflectionUtils.findField(RestTemplate.class, "requestFactory");
if (requestFactoryField != null) {
requestFactoryField.setAccessible(true);
ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) ReflectionUtils
.getField(requestFactoryField, restTemplate);
if (requestFactory instanceof HttpComponentsClientHttpRequestFactory factory) {
factory.setHttpClient(createSslHttpClient());
}
}
}
}

private HttpClient createSslHttpClient() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
Expand All @@ -30,6 +31,11 @@
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Dave Syer
* @author Nikola Kološnjaji
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WiremockTestsApplication.class,
properties = "app.baseUrl=https://localhost:${wiremock.server.https-port}",
Expand All @@ -50,6 +56,20 @@ public void contextLoads() throws Exception {
assertThat(this.service.go()).isEqualTo("Hello World!");
}

@Test
public void contextLoadsWithApacheClient() throws Exception {
stubFor(get(urlEqualTo("/test"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.goWithApacheClient()).isEqualTo("Hello World!");
}

@Test
public void contextLoadsWithApacheClientAndAdditonalInterceptor() throws Exception {
stubFor(get(urlEqualTo("/test"))
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
assertThat(this.service.goWithApacheClientAndAdditonalInterceptor()).isEqualTo("Hello World!");
}

@Test
public void portsAreFixed() {
boolean httpPortDynamic = this.wireMockProperties.getServer().isPortDynamic();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,47 +22,51 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
* @author Dave Syer
* @author Nikola Kološnjaji
*
*/
@Configuration
@EnableAutoConfiguration
@Import({ Service.class, Controller.class })
@Import(Service.class)
public class WiremockTestsApplication {

public static void main(String[] args) {
SpringApplication.run(WiremockTestsApplication.class, args);
}

@Bean
@Primary
public RestTemplate restTemplate() {
return new RestTemplate();
}

}

@RestController
class Controller {

private final Service service;

Controller(Service service) {
this.service = service;
@Bean
public RestTemplate apacheHttpClient(RestTemplateBuilder builder) {
return builder.requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build();
}

@RequestMapping("/")
public String home() {
return this.service.go();
@Bean
public RestTemplate apacheHttpClientWithInterceptor(RestTemplateBuilder builder) {
return builder.requestFactory(() -> new HttpComponentsClientHttpRequestFactory())
.additionalInterceptors(new BasicAuthenticationInterceptor("u", "p")).build();
}

}
Expand All @@ -77,8 +81,15 @@ class Service {

private RestTemplate restTemplate;

Service(RestTemplate restTemplate) {
private RestTemplate apacheHttpClient;

private RestTemplate apacheHttpClientWithInterceptor;

Service(RestTemplate restTemplate, @Qualifier("apacheHttpClient") RestTemplate apacheHttpClient,
@Qualifier("apacheHttpClientWithInterceptor") RestTemplate apacheHttpClientWithInterceptor) {
this.restTemplate = restTemplate;
this.apacheHttpClient = apacheHttpClient;
this.apacheHttpClientWithInterceptor = apacheHttpClientWithInterceptor;
}

public String go() {
Expand All @@ -87,6 +98,18 @@ public String go() {
return this.restTemplate.getForEntity(requestUrl, String.class).getBody();
}

public String goWithApacheClient() {
String requestUrl = this.base + "/test";
log.info("Will send a request to [" + requestUrl + "]");
return this.apacheHttpClient.getForEntity(requestUrl, String.class).getBody();
}

public String goWithApacheClientAndAdditonalInterceptor() {
String requestUrl = this.base + "/test";
log.info("Will send a request to [" + requestUrl + "]");
return this.apacheHttpClientWithInterceptor.getForEntity(requestUrl, String.class).getBody();
}

public String link() {
String requestUrl = this.base + "/link";
log.info("Will send a request to [" + requestUrl + "]");
Expand Down