From 5343cb9b35b079573dcf5fb552b8ad8275236f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Kolo=C5=A1njaji?= Date: Tue, 21 Mar 2023 15:18:19 +0100 Subject: [PATCH] Relaxed SSL validation with RestTemplate interceptors (#1869) * Relaxed SSL validation with RestTemplate interceptors * Formatting * Checkstyle fixes #1868 --- .../WireMockRestTemplateConfiguration.java | 21 ++++++- ...gureWireMockHttpsPortApplicationTests.java | 19 +++++++ .../wiremock/WiremockTestsApplication.java | 55 +++++++++++++------ 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java index 4f245d1d00..d0fcd12cd1 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockRestTemplateConfiguration.java @@ -16,6 +16,8 @@ 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; @@ -29,13 +31,17 @@ import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.InterceptingClientHttpRequestFactory; +import org.springframework.util.ReflectionUtils; import org.springframework.web.client.RestTemplate; import static org.apache.hc.client5.http.ssl.NoopHostnameVerifier.INSTANCE; /** * @author Dave Syer + * @author Nikola Kološnjaji * */ @Configuration(proxyBeanMethods = false) @@ -48,11 +54,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) { + 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() { diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockHttpsPortApplicationTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockHttpsPortApplicationTests.java index 40a6ab596d..65c14a4654 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockHttpsPortApplicationTests.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockHttpsPortApplicationTests.java @@ -30,6 +30,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}", @@ -50,6 +55,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(); diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java index 2a05d26a7c..759673f113 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java @@ -22,22 +22,30 @@ 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) { @@ -45,24 +53,20 @@ public static void main(String[] 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(); } } @@ -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() { @@ -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 + "]");