Adds support for Tls to config data.
Moves common setup to new ConfigClientRequestTemplateFactory class that was replicated between two implementations. Fixes gh-1689
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.config.client.tls;
|
||||
|
||||
public class ConfigClientConfigDataTlsTests extends ConfigClientTlsTests {
|
||||
|
||||
@Override
|
||||
protected TlsConfigClientRunner createConfigClient(boolean optional) {
|
||||
String importValue = "configserver:";
|
||||
if (optional) {
|
||||
importValue = "optional:" + importValue;
|
||||
}
|
||||
return new TlsConfigClientRunner(TestApp.class, server, "spring.config.import", importValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TlsConfigClientRunner createConfigClient() {
|
||||
return new TlsConfigClientRunner(TestApp.class, server, "spring.config.import", "optional:configserver:");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ConfigClientTlsTests extends AbstractTlsSetup {
|
||||
|
||||
private static TlsConfigServerRunner server;
|
||||
protected static TlsConfigServerRunner server;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupAll() throws Exception {
|
||||
@@ -95,7 +95,7 @@ public class ConfigClientTlsTests extends AbstractTlsSetup {
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void wrongPasswordCauseFailure() {
|
||||
TlsConfigClientRunner client = createConfigClient();
|
||||
TlsConfigClientRunner client = createConfigClient(false);
|
||||
enableTlsClient(client);
|
||||
client.setKeyStore(clientCert, WRONG_PASSWORD, WRONG_PASSWORD);
|
||||
client.start();
|
||||
@@ -103,7 +103,7 @@ public class ConfigClientTlsTests extends AbstractTlsSetup {
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void nonExistKeyStoreCauseFailure() {
|
||||
TlsConfigClientRunner client = createConfigClient();
|
||||
TlsConfigClientRunner client = createConfigClient(false);
|
||||
enableTlsClient(client);
|
||||
client.setKeyStore(new File("nonExistFile"));
|
||||
client.start();
|
||||
@@ -119,7 +119,15 @@ public class ConfigClientTlsTests extends AbstractTlsSetup {
|
||||
}
|
||||
}
|
||||
|
||||
private TlsConfigClientRunner createConfigClient() {
|
||||
protected TlsConfigClientRunner createConfigClient(boolean optional) {
|
||||
TlsConfigClientRunner runner = createConfigClient();
|
||||
if (!optional) {
|
||||
runner.property("spring.cloud.config.fail-fast", "true");
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
|
||||
protected TlsConfigClientRunner createConfigClient() {
|
||||
return new TlsConfigClientRunner(TestApp.class, server);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,15 @@ import java.io.File;
|
||||
public class TlsConfigClientRunner extends AppRunner {
|
||||
|
||||
public TlsConfigClientRunner(Class<?> appClass, AppRunner server) {
|
||||
this(appClass, server, "spring.config.use-legacy-processing", "true");
|
||||
}
|
||||
|
||||
public TlsConfigClientRunner(Class<?> appClass, AppRunner server, String importKey, String importValue) {
|
||||
super(appClass);
|
||||
|
||||
property("spring.cloud.config.uri", server.root());
|
||||
property("spring.cloud.config.enabled", "true");
|
||||
property("spring.config.use-legacy-processing", "true");
|
||||
property(importKey, importValue);
|
||||
}
|
||||
|
||||
public void enableTls() {
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
|
||||
import org.springframework.cloud.configuration.SSLContextFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
|
||||
|
||||
public class ConfigClientRequestTemplateFactory {
|
||||
|
||||
private final Log log;
|
||||
|
||||
private final ConfigClientProperties properties;
|
||||
|
||||
public ConfigClientRequestTemplateFactory(Log log, ConfigClientProperties properties) {
|
||||
this.log = log;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public Log getLog() {
|
||||
return this.log;
|
||||
}
|
||||
|
||||
public ConfigClientProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
public RestTemplate create() {
|
||||
if (properties.getRequestReadTimeout() < 0) {
|
||||
throw new IllegalStateException("Invalid Value for Read Timeout set.");
|
||||
}
|
||||
if (properties.getRequestConnectTimeout() < 0) {
|
||||
throw new IllegalStateException("Invalid Value for Connect Timeout set.");
|
||||
}
|
||||
|
||||
ClientHttpRequestFactory requestFactory = createHttpRequestFactory(properties);
|
||||
RestTemplate template = new RestTemplate(requestFactory);
|
||||
Map<String, String> headers = new HashMap<>(properties.getHeaders());
|
||||
headers.remove(AUTHORIZATION); // To avoid redundant addition of header
|
||||
if (!headers.isEmpty()) {
|
||||
template.setInterceptors(Arrays.asList(new GenericRequestHeaderInterceptor(headers)));
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactory createHttpRequestFactory(ConfigClientProperties client) {
|
||||
if (client.getTls().isEnabled()) {
|
||||
try {
|
||||
SSLContextFactory factory = new SSLContextFactory(client.getTls());
|
||||
SSLContext sslContext = factory.createSSLContext();
|
||||
HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
|
||||
HttpComponentsClientHttpRequestFactory result = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
result.setReadTimeout(client.getRequestReadTimeout());
|
||||
result.setConnectTimeout(client.getRequestConnectTimeout());
|
||||
return result;
|
||||
|
||||
}
|
||||
catch (GeneralSecurityException | IOException ex) {
|
||||
log.error(ex);
|
||||
throw new IllegalStateException("Failed to create config client with TLS.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleClientHttpRequestFactory result = new SimpleClientHttpRequestFactory();
|
||||
result.setReadTimeout(client.getRequestReadTimeout());
|
||||
result.setConnectTimeout(client.getRequestConnectTimeout());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addAuthorizationToken(HttpHeaders httpHeaders, String username, String password) {
|
||||
String authorization = properties.getHeaders().get(AUTHORIZATION);
|
||||
|
||||
if (password != null && authorization != null) {
|
||||
throw new IllegalStateException("You must set either 'password' or 'authorization'");
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
byte[] token = Base64Utils.encode((username + ":" + password).getBytes());
|
||||
httpHeaders.add("Authorization", "Basic " + new String(token));
|
||||
}
|
||||
else if (authorization != null) {
|
||||
httpHeaders.add("Authorization", authorization);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided headers to the request.
|
||||
*/
|
||||
public static class GenericRequestHeaderInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private final Map<String, String> headers;
|
||||
|
||||
public GenericRequestHeaderInterceptor(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
|
||||
throws IOException {
|
||||
for (Map.Entry<String, String> header : this.headers.entrySet()) {
|
||||
request.getHeaders().add(header.getKey(), header.getValue());
|
||||
}
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
|
||||
protected Map<String, String> getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,14 +46,12 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.client.ResourceAccessException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.TOKEN_HEADER;
|
||||
|
||||
@@ -240,6 +238,9 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
|
||||
ResponseEntity<Environment> response = null;
|
||||
List<MediaType> acceptHeader = Collections.singletonList(MediaType.parseMediaType(properties.getMediaType()));
|
||||
|
||||
ConfigClientRequestTemplateFactory requestTemplateFactory = context.getBootstrapContext()
|
||||
.get(ConfigClientRequestTemplateFactory.class);
|
||||
|
||||
for (int i = 0; i < noOfUrls; i++) {
|
||||
ConfigClientProperties.Credentials credentials = properties.getCredentials(i);
|
||||
String uri = credentials.getUri();
|
||||
@@ -251,7 +252,7 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setAccept(acceptHeader);
|
||||
addAuthorizationToken(properties, headers, username, password);
|
||||
requestTemplateFactory.addAuthorizationToken(headers, username, password);
|
||||
if (StringUtils.hasText(token)) {
|
||||
headers.add(TOKEN_HEADER, token);
|
||||
}
|
||||
@@ -288,22 +289,9 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void addAuthorizationToken(ConfigClientProperties configClientProperties, HttpHeaders httpHeaders,
|
||||
String username, String password) {
|
||||
String authorization = configClientProperties.getHeaders().get(AUTHORIZATION);
|
||||
|
||||
if (password != null && authorization != null) {
|
||||
throw new IllegalStateException("You must set either 'password' or 'authorization'");
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
byte[] token = Base64Utils.encode((username + ":" + password).getBytes());
|
||||
httpHeaders.add("Authorization", "Basic " + new String(token));
|
||||
}
|
||||
else if (authorization != null) {
|
||||
httpHeaders.add("Authorization", authorization);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ package org.springframework.cloud.config.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
@@ -38,12 +36,10 @@ import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.CONFIG_DISCOVERY_ENABLED;
|
||||
|
||||
public class ConfigServerConfigDataLocationResolver
|
||||
@@ -84,27 +80,9 @@ public class ConfigServerConfigDataLocationResolver
|
||||
return context.getBootstrapContext().getOrElse(BindHandler.class, null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected RestTemplate createRestTemplate(ConfigClientProperties properties) {
|
||||
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
||||
if (properties.getRequestReadTimeout() < 0) {
|
||||
throw new IllegalStateException("Invalid Value for Read Timeout set.");
|
||||
}
|
||||
if (properties.getRequestConnectTimeout() < 0) {
|
||||
throw new IllegalStateException("Invalid Value for Connect Timeout set.");
|
||||
}
|
||||
requestFactory.setReadTimeout(properties.getRequestReadTimeout());
|
||||
requestFactory.setConnectTimeout(properties.getRequestConnectTimeout());
|
||||
RestTemplate template = new RestTemplate(requestFactory);
|
||||
Map<String, String> headers = new HashMap<>(properties.getHeaders());
|
||||
if (headers.containsKey(AUTHORIZATION)) {
|
||||
headers.remove(AUTHORIZATION); // To avoid redundant addition of header
|
||||
}
|
||||
if (!headers.isEmpty()) {
|
||||
template.setInterceptors(Collections
|
||||
.singletonList(new ConfigServicePropertySourceLocator.GenericRequestHeaderInterceptor(headers)));
|
||||
}
|
||||
|
||||
return template;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Log getLog() {
|
||||
@@ -147,9 +125,17 @@ public class ConfigServerConfigDataLocationResolver
|
||||
bootstrapContext.addCloseListener(event -> event.getApplicationContext().getBeanFactory().registerSingleton(
|
||||
"configDataConfigClientProperties", event.getBootstrapContext().get(ConfigClientProperties.class)));
|
||||
|
||||
bootstrapContext.registerIfAbsent(ConfigClientRequestTemplateFactory.class,
|
||||
context -> new ConfigClientRequestTemplateFactory(log, context.get(ConfigClientProperties.class)));
|
||||
|
||||
bootstrapContext.registerIfAbsent(RestTemplate.class, context -> {
|
||||
ConfigClientProperties props = context.get(ConfigClientProperties.class);
|
||||
return createRestTemplate(props);
|
||||
ConfigClientRequestTemplateFactory factory = context.get(ConfigClientRequestTemplateFactory.class);
|
||||
RestTemplate restTemplate = createRestTemplate(factory.getProperties());
|
||||
if (restTemplate != null) {
|
||||
// shouldn't normally happen
|
||||
return restTemplate;
|
||||
}
|
||||
return factory.create();
|
||||
});
|
||||
|
||||
boolean discoveryEnabled = resolverContext.getBinder()
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -25,14 +23,9 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
|
||||
import org.springframework.boot.env.OriginTrackedMapPropertySource;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
@@ -42,33 +35,23 @@ import org.springframework.cloud.bootstrap.support.OriginTrackedCompositePropert
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.configuration.SSLContextFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.client.ResourceAccessException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.TOKEN_HEADER;
|
||||
|
||||
@@ -95,7 +78,9 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
public org.springframework.core.env.PropertySource<?> locate(org.springframework.core.env.Environment environment) {
|
||||
ConfigClientProperties properties = this.defaultProperties.override(environment);
|
||||
CompositePropertySource composite = new OriginTrackedCompositePropertySource("configService");
|
||||
RestTemplate restTemplate = this.restTemplate == null ? getSecureRestTemplate(properties) : this.restTemplate;
|
||||
ConfigClientRequestTemplateFactory requestTemplateFactory = new ConfigClientRequestTemplateFactory(logger,
|
||||
properties);
|
||||
|
||||
Exception error = null;
|
||||
String errorBody = null;
|
||||
try {
|
||||
@@ -106,7 +91,7 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
String state = ConfigClientStateHolder.getState();
|
||||
// Try all the labels until one works
|
||||
for (String label : labels) {
|
||||
Environment result = getRemoteEnvironment(restTemplate, properties, label.trim(), state);
|
||||
Environment result = getRemoteEnvironment(requestTemplateFactory, label.trim(), state);
|
||||
if (result != null) {
|
||||
log(result);
|
||||
|
||||
@@ -209,8 +194,10 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
}
|
||||
}
|
||||
|
||||
private Environment getRemoteEnvironment(RestTemplate restTemplate, ConfigClientProperties properties, String label,
|
||||
private Environment getRemoteEnvironment(ConfigClientRequestTemplateFactory requestTemplateFactory, String label,
|
||||
String state) {
|
||||
RestTemplate restTemplate = this.restTemplate == null ? requestTemplateFactory.create() : this.restTemplate;
|
||||
ConfigClientProperties properties = requestTemplateFactory.getProperties();
|
||||
String path = "/{name}/{profile}";
|
||||
String name = properties.getName();
|
||||
String profile = properties.getProfile();
|
||||
@@ -241,7 +228,7 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setAccept(acceptHeader);
|
||||
addAuthorizationToken(properties, headers, username, password);
|
||||
requestTemplateFactory.addAuthorizationToken(headers, username, password);
|
||||
if (StringUtils.hasText(token)) {
|
||||
headers.add(TOKEN_HEADER, token);
|
||||
}
|
||||
@@ -282,93 +269,15 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
private RestTemplate getSecureRestTemplate(ConfigClientProperties client) {
|
||||
if (client.getRequestReadTimeout() < 0) {
|
||||
throw new IllegalStateException("Invalid Value for Read Timeout set.");
|
||||
}
|
||||
if (client.getRequestConnectTimeout() < 0) {
|
||||
throw new IllegalStateException("Invalid Value for Connect Timeout set.");
|
||||
}
|
||||
|
||||
ClientHttpRequestFactory requestFactory = createHttpRquestFactory(client);
|
||||
RestTemplate template = new RestTemplate(requestFactory);
|
||||
Map<String, String> headers = new HashMap<>(client.getHeaders());
|
||||
if (headers.containsKey(AUTHORIZATION)) {
|
||||
headers.remove(AUTHORIZATION); // To avoid redundant addition of header
|
||||
}
|
||||
if (!headers.isEmpty()) {
|
||||
template.setInterceptors(
|
||||
Arrays.<ClientHttpRequestInterceptor>asList(new GenericRequestHeaderInterceptor(headers)));
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactory createHttpRquestFactory(ConfigClientProperties client) {
|
||||
if (client.getTls().isEnabled()) {
|
||||
try {
|
||||
SSLContextFactory factory = new SSLContextFactory(client.getTls());
|
||||
SSLContext sslContext = factory.createSSLContext();
|
||||
HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
|
||||
HttpComponentsClientHttpRequestFactory result = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
result.setReadTimeout(client.getRequestReadTimeout());
|
||||
result.setConnectTimeout(client.getRequestConnectTimeout());
|
||||
return result;
|
||||
|
||||
}
|
||||
catch (GeneralSecurityException | IOException ex) {
|
||||
logger.error(ex);
|
||||
throw new IllegalStateException("Failed to create config client with TLS.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleClientHttpRequestFactory result = new SimpleClientHttpRequestFactory();
|
||||
result.setReadTimeout(client.getRequestReadTimeout());
|
||||
result.setConnectTimeout(client.getRequestConnectTimeout());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addAuthorizationToken(ConfigClientProperties configClientProperties, HttpHeaders httpHeaders,
|
||||
String username, String password) {
|
||||
String authorization = configClientProperties.getHeaders().get(AUTHORIZATION);
|
||||
|
||||
if (password != null && authorization != null) {
|
||||
throw new IllegalStateException("You must set either 'password' or 'authorization'");
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
byte[] token = Base64Utils.encode((username + ":" + password).getBytes());
|
||||
httpHeaders.add("Authorization", "Basic " + new String(token));
|
||||
}
|
||||
else if (authorization != null) {
|
||||
httpHeaders.add("Authorization", authorization);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided headers to the request.
|
||||
*/
|
||||
public static class GenericRequestHeaderInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private final Map<String, String> headers;
|
||||
@Deprecated
|
||||
public static class GenericRequestHeaderInterceptor
|
||||
extends ConfigClientRequestTemplateFactory.GenericRequestHeaderInterceptor {
|
||||
|
||||
public GenericRequestHeaderInterceptor(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
|
||||
throws IOException {
|
||||
for (Entry<String, String> header : this.headers.entrySet()) {
|
||||
request.getHeaders().add(header.getKey(), header.getValue());
|
||||
}
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
|
||||
protected Map<String, String> getHeaders() {
|
||||
return this.headers;
|
||||
super(headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -34,7 +35,7 @@ import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator.GenericRequestHeaderInterceptor;
|
||||
import org.springframework.cloud.config.client.ConfigClientRequestTemplateFactory.GenericRequestHeaderInterceptor;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
@@ -51,7 +52,6 @@ import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -227,8 +227,7 @@ public class ConfigServicePropertySourceLocatorTests {
|
||||
byte[] body = new byte[] {};
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-Example-Version", "2.1");
|
||||
new ConfigServicePropertySourceLocator.GenericRequestHeaderInterceptor(headers).intercept(request, body,
|
||||
execution);
|
||||
new GenericRequestHeaderInterceptor(headers).intercept(request, body, execution);
|
||||
Mockito.verify(execution).execute(request, body);
|
||||
assertThat(request.getHeaders().getFirst("X-Example-Version")).isEqualTo("2.1");
|
||||
}
|
||||
@@ -237,10 +236,9 @@ public class ConfigServicePropertySourceLocatorTests {
|
||||
public void shouldAddAuthorizationHeaderWhenPasswordSet() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
|
||||
this.locator = new ConfigServicePropertySourceLocator(defaults);
|
||||
String username = "user";
|
||||
String password = "pass";
|
||||
ReflectionTestUtils.invokeMethod(this.locator, "addAuthorizationToken", defaults, headers, username, password);
|
||||
factory(defaults).addAuthorizationToken(headers, username, password);
|
||||
assertThat(headers).hasSize(1);
|
||||
}
|
||||
|
||||
@@ -249,10 +247,9 @@ public class ConfigServicePropertySourceLocatorTests {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
|
||||
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
|
||||
this.locator = new ConfigServicePropertySourceLocator(defaults);
|
||||
String username = "user";
|
||||
String password = null;
|
||||
ReflectionTestUtils.invokeMethod(this.locator, "addAuthorizationToken", defaults, headers, username, password);
|
||||
factory(defaults).addAuthorizationToken(headers, username, password);
|
||||
assertThat(headers).hasSize(1);
|
||||
}
|
||||
|
||||
@@ -261,32 +258,29 @@ public class ConfigServicePropertySourceLocatorTests {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
|
||||
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
|
||||
this.locator = new ConfigServicePropertySourceLocator(defaults);
|
||||
String username = "user";
|
||||
String password = "pass";
|
||||
this.expected.expect(IllegalStateException.class);
|
||||
this.expected.expectMessage("You must set either 'password' or 'authorization'");
|
||||
ReflectionTestUtils.invokeMethod(this.locator, "addAuthorizationToken", defaults, headers, username, password);
|
||||
factory(defaults).addAuthorizationToken(headers, username, password);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowExceptionWhenNegativeReadTimeoutSet() {
|
||||
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
|
||||
defaults.setRequestReadTimeout(-1);
|
||||
this.locator = new ConfigServicePropertySourceLocator(defaults);
|
||||
this.expected.expect(IllegalStateException.class);
|
||||
this.expected.expectMessage("Invalid Value for Read Timeout set.");
|
||||
ReflectionTestUtils.invokeMethod(this.locator, "getSecureRestTemplate", defaults);
|
||||
factory(defaults).create();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowExceptionWhenNegativeConnectTimeoutSet() {
|
||||
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
|
||||
defaults.setRequestConnectTimeout(-1);
|
||||
this.locator = new ConfigServicePropertySourceLocator(defaults);
|
||||
this.expected.expect(IllegalStateException.class);
|
||||
this.expected.expectMessage("Invalid Value for Connect Timeout set.");
|
||||
ReflectionTestUtils.invokeMethod(this.locator, "getSecureRestTemplate", defaults);
|
||||
factory(defaults).create();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,8 +288,7 @@ public class ConfigServicePropertySourceLocatorTests {
|
||||
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
|
||||
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
|
||||
defaults.getHeaders().put("key", "value");
|
||||
this.locator = new ConfigServicePropertySourceLocator(defaults);
|
||||
RestTemplate restTemplate = ReflectionTestUtils.invokeMethod(this.locator, "getSecureRestTemplate", defaults);
|
||||
RestTemplate restTemplate = factory(defaults).create();
|
||||
Iterator<ClientHttpRequestInterceptor> iterator = restTemplate.getInterceptors().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
GenericRequestHeaderInterceptor genericRequestHeaderInterceptor = (GenericRequestHeaderInterceptor) iterator
|
||||
@@ -304,6 +297,10 @@ public class ConfigServicePropertySourceLocatorTests {
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigClientRequestTemplateFactory factory(ConfigClientProperties properties) {
|
||||
return new ConfigClientRequestTemplateFactory(LogFactory.getLog(getClass()), properties);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "raw" })
|
||||
@Test
|
||||
public void shouldPreserveOrder() {
|
||||
|
||||
Reference in New Issue
Block a user