Introduce RestTemplateBuilder and WebClientBuilder.

VaultTemplate and ReactiveVaultTemplate can now be constructed by using RestTemplateBuilder respective WebClientBuilder.

These client builders allow for improved customization of RestTemplate and WebClient to consistently modify outgoing requests by adding headers or interceptors/filter-functions.

ClientHttpConnectorFactory and ClientHttpRequestFactoryFactory are moved from config to client packages and we introduced deprecated forwarding-stubs in their old place to retain compatibility.

Closes gh-423.
This commit is contained in:
Mark Paluch
2019-06-28 16:37:59 +02:00
parent 92bdb2488c
commit 5a47421a03
19 changed files with 1409 additions and 505 deletions

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2017-2019 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.vault.client;
import java.io.IOException;
import java.security.GeneralSecurityException;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
import reactor.netty.http.client.HttpClient;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import static org.springframework.vault.client.ClientHttpRequestFactoryFactory.createKeyManagerFactory;
import static org.springframework.vault.client.ClientHttpRequestFactoryFactory.createTrustManagerFactory;
import static org.springframework.vault.client.ClientHttpRequestFactoryFactory.hasSslConfiguration;
/**
* Factory for {@link ClientHttpConnector} that supports
* {@link ReactorClientHttpConnector}.
*
* @author Mark Paluch
* @since 2.2
*/
public class ClientHttpConnectorFactory {
/**
* Create a {@link ClientHttpConnector} for the given {@link ClientOptions} and
* {@link SslConfiguration}.
*
* @param options must not be {@literal null}
* @param sslConfiguration must not be {@literal null}
* @return a new {@link ClientHttpConnector}.
*/
public static ClientHttpConnector create(ClientOptions options,
SslConfiguration sslConfiguration) {
HttpClient client = HttpClient.create();
if (hasSslConfiguration(sslConfiguration)) {
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
configureSsl(sslConfiguration, sslContextBuilder);
client = client.secure(builder -> {
builder.sslContext(sslContextBuilder);
});
}
client = client.tcpConfiguration(it -> it.option(
ChannelOption.CONNECT_TIMEOUT_MILLIS,
Math.toIntExact(options.getConnectionTimeout().toMillis())));
return new ReactorClientHttpConnector(client);
}
private static void configureSsl(SslConfiguration sslConfiguration,
SslContextBuilder sslContextBuilder) {
try {
if (sslConfiguration.getTrustStoreConfiguration().isPresent()) {
sslContextBuilder.trustManager(createTrustManagerFactory(sslConfiguration
.getTrustStoreConfiguration()));
}
if (sslConfiguration.getKeyStoreConfiguration().isPresent()) {
sslContextBuilder.keyManager(createKeyManagerFactory(
sslConfiguration.getKeyStoreConfiguration(),
sslConfiguration.getKeyConfiguration()));
}
}
catch (GeneralSecurityException | IOException e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -0,0 +1,456 @@
/*
* Copyright 2016-2019 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.vault.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.ProxySelector;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import okhttp3.OkHttpClient.Builder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.DefaultSchemePortResolver;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.support.SslConfiguration.KeyStoreConfiguration;
import static org.springframework.vault.support.SslConfiguration.KeyConfiguration;
/**
* Factory for {@link ClientHttpRequestFactory} that supports Apache HTTP Components,
* OkHttp, Netty and the JDK HTTP client (in that order). This factory configures a
* {@link ClientHttpRequestFactory} depending on the available dependencies.
*
* @author Mark Paluch
* @since 2.2
*/
public class ClientHttpRequestFactoryFactory {
private static final Log logger = LogFactory
.getLog(ClientHttpRequestFactoryFactory.class);
private static final boolean HTTP_COMPONENTS_PRESENT = isPresent("org.apache.http.client.HttpClient");
private static final boolean OKHTTP3_PRESENT = isPresent("okhttp3.OkHttpClient");
private static final boolean NETTY_PRESENT = isPresent(
"io.netty.channel.nio.NioEventLoopGroup", "io.netty.handler.ssl.SslContext",
"io.netty.handler.codec.http.HttpClientCodec");
/**
* Checks for presence of all {@code classNames} using this class' classloader.
*
* @param classNames
* @return {@literal true} if all classes are present; {@literal false} if at least
* one class cannot be found.
*/
private static boolean isPresent(String... classNames) {
for (String className : classNames) {
if (!ClassUtils.isPresent(className,
ClientHttpRequestFactoryFactory.class.getClassLoader())) {
return false;
}
}
return true;
}
/**
* Create a {@link ClientHttpRequestFactory} for the given {@link ClientOptions} and
* {@link SslConfiguration}.
*
* @param options must not be {@literal null}
* @param sslConfiguration must not be {@literal null}
* @return a new {@link ClientHttpRequestFactory}. Lifecycle beans must be initialized
* after obtaining.
*/
public static ClientHttpRequestFactory create(ClientOptions options,
SslConfiguration sslConfiguration) {
Assert.notNull(options, "ClientOptions must not be null");
Assert.notNull(sslConfiguration, "SslConfiguration must not be null");
try {
if (HTTP_COMPONENTS_PRESENT) {
return HttpComponents.usingHttpComponents(options, sslConfiguration);
}
if (OKHTTP3_PRESENT) {
return OkHttp3.usingOkHttp3(options, sslConfiguration);
}
if (NETTY_PRESENT) {
return Netty.usingNetty(options, sslConfiguration);
}
}
catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
}
catch (IOException e) {
throw new IllegalStateException(e);
}
if (hasSslConfiguration(sslConfiguration)) {
logger.warn("VaultProperties has SSL configured but the SSL configuration "
+ "must be applied outside the Vault Client to use the JDK HTTP client");
}
return new SimpleClientHttpRequestFactory();
}
static SSLContext getSSLContext(SslConfiguration sslConfiguration,
TrustManager[] trustManagers) throws GeneralSecurityException, IOException {
KeyConfiguration keyConfiguration = sslConfiguration.getKeyConfiguration();
KeyManager[] keyManagers = sslConfiguration.getKeyStoreConfiguration()
.isPresent() ? createKeyManagerFactory(
sslConfiguration.getKeyStoreConfiguration(), keyConfiguration)
.getKeyManagers() : null;
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
private static TrustManager[] getTrustManagers(SslConfiguration sslConfiguration)
throws GeneralSecurityException, IOException {
return sslConfiguration.getTrustStoreConfiguration().isPresent() ? createTrustManagerFactory(
sslConfiguration.getTrustStoreConfiguration()).getTrustManagers()
: null;
}
static KeyManagerFactory createKeyManagerFactory(
KeyStoreConfiguration keyStoreConfiguration, KeyConfiguration keyConfiguration)
throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(StringUtils
.hasText(keyStoreConfiguration.getStoreType()) ? keyStoreConfiguration
.getStoreType() : KeyStore.getDefaultType());
loadKeyStore(keyStoreConfiguration, keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
char[] keyPasswordToUse = keyConfiguration.getKeyPassword();
if (keyPasswordToUse == null) {
keyPasswordToUse = keyStoreConfiguration.getStorePassword() == null ? new char[0]
: keyStoreConfiguration.getStorePassword();
}
keyManagerFactory.init(keyStore, keyPasswordToUse);
if (StringUtils.hasText(keyConfiguration.getKeyAlias())) {
return new KeySelectingKeyManagerFactory(keyManagerFactory, keyConfiguration);
}
return keyManagerFactory;
}
static TrustManagerFactory createTrustManagerFactory(
KeyStoreConfiguration keyStoreConfiguration) throws GeneralSecurityException,
IOException {
KeyStore trustStore = KeyStore.getInstance(StringUtils
.hasText(keyStoreConfiguration.getStoreType()) ? keyStoreConfiguration
.getStoreType() : KeyStore.getDefaultType());
loadKeyStore(keyStoreConfiguration, trustStore);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
return trustManagerFactory;
}
private static void loadKeyStore(KeyStoreConfiguration keyStoreConfiguration,
KeyStore keyStore) throws IOException, NoSuchAlgorithmException,
CertificateException {
InputStream inputStream = null;
try {
inputStream = keyStoreConfiguration.getResource().getInputStream();
keyStore.load(inputStream, keyStoreConfiguration.getStorePassword());
}
finally {
if (inputStream != null) {
inputStream.close();
}
}
}
static boolean hasSslConfiguration(SslConfiguration sslConfiguration) {
return sslConfiguration.getTrustStoreConfiguration().isPresent()
|| sslConfiguration.getKeyStoreConfiguration().isPresent();
}
/**
* {@link ClientHttpRequestFactory} for Apache Http Components.
*
* @author Mark Paluch
*/
static class HttpComponents {
static ClientHttpRequestFactory usingHttpComponents(ClientOptions options,
SslConfiguration sslConfiguration) throws GeneralSecurityException,
IOException {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(
DefaultSchemePortResolver.INSTANCE, ProxySelector.getDefault()));
if (hasSslConfiguration(sslConfiguration)) {
SSLContext sslContext = getSSLContext(sslConfiguration,
getTrustManagers(sslConfiguration));
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext);
httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
httpClientBuilder.setSSLContext(sslContext);
}
RequestConfig requestConfig = RequestConfig
.custom()
//
.setConnectTimeout(
Math.toIntExact(options.getConnectionTimeout().toMillis())) //
.setSocketTimeout(
Math.toIntExact(options.getReadTimeout().toMillis())) //
.setAuthenticationEnabled(true) //
.build();
httpClientBuilder.setDefaultRequestConfig(requestConfig);
// Support redirects
httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
}
}
/**
* {@link ClientHttpRequestFactory} for the {@link okhttp3.OkHttpClient}.
*
* @author Mark Paluch
*/
static class OkHttp3 {
static ClientHttpRequestFactory usingOkHttp3(ClientOptions options,
SslConfiguration sslConfiguration) throws GeneralSecurityException,
IOException {
Builder builder = new Builder();
if (hasSslConfiguration(sslConfiguration)) {
TrustManager[] trustManagers = getTrustManagers(sslConfiguration);
if (trustManagers.length != 1
|| !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
SSLContext sslContext = getSSLContext(sslConfiguration, trustManagers);
builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
}
builder.connectTimeout(options.getConnectionTimeout().toMillis(),
TimeUnit.MILLISECONDS).readTimeout(
options.getReadTimeout().toMillis(), TimeUnit.MILLISECONDS);
return new OkHttp3ClientHttpRequestFactory(builder.build());
}
}
/**
* {@link ClientHttpRequestFactory} for Netty.
*
* @author Mark Paluch
*/
static class Netty {
static ClientHttpRequestFactory usingNetty(ClientOptions options,
SslConfiguration sslConfiguration) throws GeneralSecurityException,
IOException {
final Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
if (hasSslConfiguration(sslConfiguration)) {
SslContextBuilder sslContextBuilder = SslContextBuilder //
.forClient();
if (sslConfiguration.getTrustStoreConfiguration().isPresent()) {
sslContextBuilder
.trustManager(createTrustManagerFactory(sslConfiguration
.getTrustStoreConfiguration()));
}
if (sslConfiguration.getKeyStoreConfiguration().isPresent()) {
sslContextBuilder.keyManager(createKeyManagerFactory(
sslConfiguration.getKeyStoreConfiguration(),
sslConfiguration.getKeyConfiguration()));
}
requestFactory.setSslContext(sslContextBuilder.sslProvider(
SslProvider.JDK).build());
}
requestFactory.setConnectTimeout(Math.toIntExact(options
.getConnectionTimeout().toMillis()));
requestFactory.setReadTimeout(Math.toIntExact(options.getReadTimeout()
.toMillis()));
return requestFactory;
}
}
static class KeySelectingKeyManagerFactory extends KeyManagerFactory {
KeySelectingKeyManagerFactory(KeyManagerFactory factory,
KeyConfiguration keyConfiguration) {
super(new KeyManagerFactorySpi() {
@Override
protected void engineInit(KeyStore keyStore, char[] chars)
throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException {
factory.init(keyStore, chars);
}
@Override
protected void engineInit(
ManagerFactoryParameters managerFactoryParameters)
throws InvalidAlgorithmParameterException {
factory.init(managerFactoryParameters);
}
@Override
protected KeyManager[] engineGetKeyManagers() {
KeyManager[] keyManagers = factory.getKeyManagers();
if (keyManagers.length == 1
&& keyManagers[0] instanceof X509ExtendedKeyManager) {
return new KeyManager[] { new KeySelectingX509KeyManager(
(X509ExtendedKeyManager) keyManagers[0], keyConfiguration) };
}
return keyManagers;
}
}, factory.getProvider(), factory.getAlgorithm());
}
}
private static class KeySelectingX509KeyManager extends X509ExtendedKeyManager {
private final X509ExtendedKeyManager delegate;
private final KeyConfiguration keyConfiguration;
KeySelectingX509KeyManager(X509ExtendedKeyManager delegate,
KeyConfiguration keyConfiguration) {
this.delegate = delegate;
this.keyConfiguration = keyConfiguration;
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return delegate.getClientAliases(keyType, issuers);
}
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket) {
return keyConfiguration.getKeyAlias();
}
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
SSLEngine engine) {
return keyConfiguration.getKeyAlias();
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return delegate.getServerAliases(keyType, issuers);
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return delegate.chooseServerAlias(keyType, issuers, socket);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return delegate.getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(String alias) {
return delegate.getPrivateKey(alias);
}
}
}

View File

@@ -73,6 +73,26 @@ public class ReactiveVaultClients {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
return createWebClientBuilder(endpointProvider, connector).build();
}
/**
* Create a {@link WebClient.Builder} configured with {@link VaultEndpoint} and
* {@link ClientHttpConnector}. The client accepts relative URIs without a leading
* slash that are expanded to use {@link VaultEndpoint}.
* <p>
* Requires Jackson 2 for Object-to-JSON mapping.
*
* @param endpointProvider must not be {@literal null}.
* @param connector must not be {@literal null}.
* @return the prepared {@link WebClient.Builder}.
*/
static WebClient.Builder createWebClientBuilder(
VaultEndpointProvider endpointProvider, ClientHttpConnector connector) {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
UriBuilderFactory uriBuilderFactory = VaultClients
.createUriBuilderFactory(endpointProvider);
@@ -83,7 +103,7 @@ public class ReactiveVaultClients {
cc.decoder(new ByteArrayDecoder());
cc.decoder(new Jackson2JsonDecoder());
cc.decoder(StringDecoder.allMimeTypes(false));
cc.decoder(StringDecoder.allMimeTypes());
cc.encoder(new ByteArrayEncoder());
cc.encoder(new Jackson2JsonEncoder());
@@ -91,7 +111,7 @@ public class ReactiveVaultClients {
}).build();
return WebClient.builder().uriBuilderFactory(uriBuilderFactory)
.exchangeStrategies(strategies).clientConnector(connector).build();
.exchangeStrategies(strategies).clientConnector(connector);
}
/**

View File

@@ -0,0 +1,273 @@
/*
* Copyright 2019 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.vault.client;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
/**
* Builder that can be used to configure and create a {@link RestTemplate}. Provides
* convenience methods to configure {@link #requestFactory(ClientHttpRequestFactory)
* ClientHttpRequestFactory}, {@link #errorHandler(ResponseErrorHandler) error handlers}
* and {@link #defaultHeader(String, String) default headers}.
*
* By default the built {@link RestTemplate} will attempt to use the most suitable
* {@link ClientHttpRequestFactory} using {@link ClientHttpRequestFactoryFactory#create}.
*
* @author Mark Paluch
* @since 2.2
* @see ClientHttpRequestFactoryFactory
* @see RestTemplateCustomizer
*/
public class RestTemplateBuilder {
private @Nullable VaultEndpointProvider endpointProvider;
private Supplier<ClientHttpRequestFactory> requestFactory = () -> ClientHttpRequestFactoryFactory
.create(new ClientOptions(), SslConfiguration.unconfigured());
private @Nullable ResponseErrorHandler errorHandler;
private final Map<String, String> defaultHeaders = new LinkedHashMap<>();
private final List<RestTemplateCustomizer> customizers = new ArrayList<>();
private final Set<RestTemplateRequestCustomizer<ClientHttpRequest>> requestCustomizers = new LinkedHashSet<>();
private RestTemplateBuilder() {
}
/**
* Create a new {@link RestTemplateBuilder}.
*
* @return a new {@link RestTemplateBuilder}.
*/
public static RestTemplateBuilder builder() {
return new RestTemplateBuilder();
}
/**
* Set the {@link VaultEndpoint} that should with the {@link RestTemplate}.
*
* @param endpoint the {@link VaultEndpoint} provider.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder endpoint(VaultEndpoint endpoint) {
return endpointProvider(SimpleVaultEndpointProvider.of(endpoint));
}
/**
* Set the {@link VaultEndpointProvider} that should with the {@link RestTemplate}.
*
* @param provider the {@link VaultEndpoint} provider.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder endpointProvider(VaultEndpointProvider provider) {
Assert.notNull(provider, "VaultEndpointProvider must not be null");
this.endpointProvider = provider;
return this;
}
/**
* Set the {@link ClientHttpRequestFactory} that should be used with the
* {@link RestTemplate}.
*
* @param requestFactory the request factory.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder requestFactory(ClientHttpRequestFactory requestFactory) {
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
return requestFactory(() -> requestFactory);
}
/**
* Set the {@link Supplier} of {@link ClientHttpRequestFactory} that should be called
* each time we {@link #build()} a new {@link RestTemplate} instance.
*
* @param requestFactory the supplier for the request factory.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder requestFactory(
Supplier<ClientHttpRequestFactory> requestFactory) {
Assert.notNull(requestFactory,
"Supplier of ClientHttpRequestFactory must not be null");
this.requestFactory = requestFactory;
return this;
}
/**
* Set the {@link ResponseErrorHandler} that should be used with the
* {@link RestTemplate}.
*
* @param errorHandler the error handler to use.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder errorHandler(ResponseErrorHandler errorHandler) {
Assert.notNull(errorHandler, "ErrorHandler must not be null");
this.errorHandler = errorHandler;
return this;
}
/**
* Add a default header that will be set if not already present on the outgoing
* {@link HttpRequest}.
*
* @param name the name of the header.
* @param value the header value.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder defaultHeader(String name, String value) {
Assert.hasText(name, "Header name must not be null or empty");
this.defaultHeaders.put(name, value);
return this;
}
/**
* Add the {@link RestTemplateCustomizer RestTemplateCustomizers} that should be
* applied to the {@link RestTemplate}. Customizers are applied in the order that they
* were added.
*
* @param customizer the template customizers to add.
* @return {@code this} {@link RestTemplateBuilder}.
*/
public RestTemplateBuilder customizer(RestTemplateCustomizer... customizer) {
this.customizers.addAll(Arrays.asList(customizer));
return this;
}
/**
* Add the {@link RestTemplateRequestCustomizer RestTemplateRequestCustomizers} that
* should be applied to the {@link ClientHttpRequest}. Customizers are applied in the
* order that they were added.
*
* @param requestCustomizers the request customizers to add.
* @return {@code this} {@link RestTemplateBuilder}.
*/
@SuppressWarnings("unchecked")
public RestTemplateBuilder requestCustomizers(
RestTemplateRequestCustomizer<?>... requestCustomizers) {
Assert.notNull(requestCustomizers, "RequestCustomizers must not be null");
this.requestCustomizers.addAll((List) Arrays.asList(requestCustomizers));
return this;
}
/**
* Build a new {@link RestTemplate}. {@link VaultEndpoint} must be set.
*
* Applies also {@link ResponseErrorHandler} and {@link RestTemplateCustomizer} if
* configured.
*
* @return a new {@link RestTemplate}.
*/
public RestTemplate build() {
Assert.state(this.endpointProvider != null,
"VaultEndpointProvider must not be null");
RestTemplate restTemplate = createTemplate();
if (errorHandler != null) {
restTemplate.setErrorHandler(errorHandler);
}
customizers.forEach(customizer -> customizer.customize(restTemplate));
return restTemplate;
}
/**
* Create the {@link RestTemplate} to use.
*
* @return the {@link RestTemplate} to use.
*/
protected RestTemplate createTemplate() {
ClientHttpRequestFactory requestFactory = this.requestFactory.get();
RestTemplateBuilderClientHttpRequestFactoryWrapper wrapper = new RestTemplateBuilderClientHttpRequestFactoryWrapper(
requestFactory, new LinkedHashMap<>(defaultHeaders), new LinkedHashSet<>(
requestCustomizers));
return VaultClients.createRestTemplate(endpointProvider, wrapper);
}
static class RestTemplateBuilderClientHttpRequestFactoryWrapper extends
AbstractClientHttpRequestFactoryWrapper {
private final Map<String, String> defaultHeaders;
private final Set<RestTemplateRequestCustomizer<ClientHttpRequest>> requestCustomizers;
RestTemplateBuilderClientHttpRequestFactoryWrapper(
ClientHttpRequestFactory requestFactory,
Map<String, String> defaultHeaders,
Set<RestTemplateRequestCustomizer<ClientHttpRequest>> requestCustomizers) {
super(requestFactory);
this.defaultHeaders = defaultHeaders;
this.requestCustomizers = requestCustomizers;
}
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod,
ClientHttpRequestFactory requestFactory) throws IOException {
ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
this.defaultHeaders.forEach(headers::addIfAbsent);
this.requestCustomizers.forEach(it -> it.customize(request));
return request;
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2019 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.vault.client;
import org.springframework.web.client.RestTemplate;
/**
* Callback interface that can be used to customize a {@link RestTemplate}.
*
* @author Mark Paluch
* @since 2.2
* @see RestTemplateBuilder
*/
@FunctionalInterface
public interface RestTemplateCustomizer {
/**
* Callback to customize a {@link RestTemplate} instance.
*
* @param restTemplate the template to customize.
*/
void customize(RestTemplate restTemplate);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2019 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.vault.client;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.web.client.RestTemplate;
/**
* Callback interface that can be used to customize the {@link ClientHttpRequest} sent
* from a {@link RestTemplate}.
*
* @param <T> the {@link ClientHttpRequest} type.
* @since 2.2
* @see RestTemplateBuilder
*/
@FunctionalInterface
public interface RestTemplateRequestCustomizer<T extends ClientHttpRequest> {
/**
* Customize the specified {@link ClientHttpRequest}.
*
* @param request the request to customize.
*/
void customize(T request);
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright 2019 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.vault.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Builder that can be used to configure and create a {@link WebClient}. Provides
* convenience methods to configure {@link #httpConnector(ClientHttpConnector)
* ClientHttpConnector} and {@link #defaultHeader(String, String) default headers}.
*
* By default the built {@link WebClient} will attempt to use the most suitable
* {@link ClientHttpConnector} using {@link ClientHttpConnectorFactory#create}.
*
* @author Mark Paluch
* @since 2.2
* @see ClientHttpConnectorFactory
* @see WebClientCustomizer
*/
public class WebClientBuilder {
private @Nullable VaultEndpointProvider endpointProvider;
private Supplier<ClientHttpConnector> httpConnector = () -> ClientHttpConnectorFactory
.create(new ClientOptions(), SslConfiguration.unconfigured());
private final Map<String, String> defaultHeaders = new LinkedHashMap<>();
private final List<WebClientCustomizer> customizers = new ArrayList<>();
private final Set<ExchangeFilterFunction> filterFunctions = new LinkedHashSet<>();
private WebClientBuilder() {
}
/**
* Create a new {@link WebClientBuilder}.
*
* @return a new {@link WebClientBuilder}.
*/
public static WebClientBuilder builder() {
return new WebClientBuilder();
}
/**
* Set the {@link VaultEndpoint} that should with the {@link WebClient}.
*
* @param endpoint the {@link VaultEndpoint} provider.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder endpoint(VaultEndpoint endpoint) {
return endpointProvider(SimpleVaultEndpointProvider.of(endpoint));
}
/**
* Set the {@link VaultEndpointProvider} that should with the {@link WebClient}.
*
* @param provider the {@link VaultEndpoint} provider.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder endpointProvider(VaultEndpointProvider provider) {
Assert.notNull(provider, "VaultEndpointProvider must not be null");
this.endpointProvider = provider;
return this;
}
/**
* Set the {@link ClientHttpConnector} that should be used each with the
* {@link WebClient}.
*
* @param httpConnector the HTTP connector.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder httpConnector(ClientHttpConnector httpConnector) {
Assert.notNull(httpConnector, "ClientHttpConnector must not be null");
return requestFactory(() -> httpConnector);
}
/**
* Set the {@link Supplier} of {@link ClientHttpConnector} that should be called each
* time we {@link #build()} a new {@link WebClient} instance.
*
* @param httpConnector the supplier for the HTTP connector.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder requestFactory(Supplier<ClientHttpConnector> httpConnector) {
Assert.notNull(httpConnector, "Supplier of ClientHttpConnector must not be null");
this.httpConnector = httpConnector;
return this;
}
/**
* Add a default header that will be set if not already present on the outgoing
* {@link HttpRequest}.
*
* @param name the name of the header.
* @param value the header value.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder defaultHeader(String name, String value) {
Assert.hasText(name, "Header name must not be null or empty");
this.defaultHeaders.put(name, value);
return this;
}
/**
* Add the {@link WebClientCustomizer WebClientCustomizers} that should be applied to
* the {@link WebClient}. Customizers are applied in the order that they were added.
*
* @param customizer the client customizers to add.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder customizer(WebClientCustomizer... customizer) {
this.customizers.addAll(Arrays.asList(customizer));
return this;
}
/**
* Add the {@link ExchangeFilterFunction ExchangeFilterFunctions} that should be
* applied to the {@link ClientRequest}. {@link ExchangeFilterFunction}s are applied
* in the order that they were added.
*
* @param filterFunctions the request customizers to add.
* @return {@code this} {@link WebClientBuilder}.
*/
public WebClientBuilder filter(ExchangeFilterFunction... filterFunctions) {
Assert.notNull(filterFunctions, "ExchangeFilterFunctions must not be null");
this.filterFunctions.addAll(Arrays.asList(filterFunctions));
return this;
}
/**
* Build a new {@link WebClient}. {@link VaultEndpoint} must be set.
*
* Applies also {@link ExchangeFilterFunction} and {@link WebClientCustomizer} if
* configured.
*
* @return a new {@link WebClient}.
*/
public WebClient build() {
Assert.state(this.endpointProvider != null,
"VaultEndpointProvider must not be null");
WebClient.Builder builder = createWebClientBuilder();
if (!defaultHeaders.isEmpty()) {
Map<String, String> defaultHeaders = this.defaultHeaders;
builder.filter((request, next) -> {
return next.exchange(ClientRequest.from(request)
.headers(headers -> defaultHeaders.forEach(headers::addIfAbsent))
.build());
});
}
builder.filters(exchangeFilterFunctions -> exchangeFilterFunctions
.addAll(this.filterFunctions));
customizers.forEach(customizer -> customizer.customize(builder));
return builder.build();
}
/**
* Create the {@link WebClient.Builder} to use.
*
* @return the {@link WebClient.Builder} to use.
*/
protected WebClient.Builder createWebClientBuilder() {
ClientHttpConnector connector = this.httpConnector.get();
return ReactiveVaultClients.createWebClientBuilder(endpointProvider, connector);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2019 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.vault.client;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Callback interface that can be used to customize a {@link WebClient.Builder}.
*
* @author Mark Paluch
* @since 2.2
* @see WebClientBuilder
*/
@FunctionalInterface
public interface WebClientCustomizer {
/**
* Callback to customize a {@link WebClient.Builder} instance.
*
* @param webClientBuilder the client builder to customize.
*/
void customize(WebClient.Builder webClientBuilder);
}

View File

@@ -33,7 +33,10 @@ import org.springframework.vault.authentication.ReactiveSessionManager;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.authentication.VaultTokenSupplier;
import org.springframework.vault.client.ClientHttpConnectorFactory;
import org.springframework.vault.client.ReactiveVaultClients;
import org.springframework.vault.client.VaultEndpointProvider;
import org.springframework.vault.client.WebClientBuilder;
import org.springframework.vault.core.ReactiveVaultTemplate;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.VaultToken;
@@ -62,6 +65,21 @@ import org.springframework.web.reactive.function.client.WebClient;
public abstract class AbstractReactiveVaultConfiguration extends
AbstractVaultConfiguration {
/**
* Create a {@link WebClientBuilder} initialized with {@link VaultEndpointProvider}
* and {@link ClientHttpConnector}. May be overridden by subclasses.
*
* @return the {@link WebClientBuilder}.
* @see #vaultEndpointProvider()
* @see #clientHttpConnector()
* @since 2.2
*/
protected WebClientBuilder webClientBuilder(VaultEndpointProvider endpointProvider,
ClientHttpConnector httpConnector) {
return WebClientBuilder.builder().endpointProvider(endpointProvider)
.httpConnector(httpConnector);
}
/**
* Create a {@link ReactiveVaultTemplate}.
*
@@ -72,8 +90,8 @@ public abstract class AbstractReactiveVaultConfiguration extends
*/
@Bean
public ReactiveVaultTemplate reactiveVaultTemplate() {
return new ReactiveVaultTemplate(vaultEndpoint(), clientHttpConnector(),
reactiveSessionManager());
return new ReactiveVaultTemplate(webClientBuilder(vaultEndpointProvider(),
clientHttpConnector()), reactiveSessionManager());
}
/**

View File

@@ -30,6 +30,8 @@ import org.springframework.util.Assert;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
import org.springframework.vault.client.RestTemplateBuilder;
import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
@@ -75,6 +77,22 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw
*/
public abstract ClientAuthentication clientAuthentication();
/**
* Create a {@link RestTemplateBuilder} initialized with {@link VaultEndpointProvider}
* and {@link ClientHttpRequestFactory}. May be overridden by subclasses.
*
* @return the {@link RestTemplateBuilder}.
* @see #vaultEndpointProvider()
* @see #clientHttpRequestFactoryWrapper()
* @since 2.2
*/
protected RestTemplateBuilder restTemplateBuilder(
VaultEndpointProvider endpointProvider,
ClientHttpRequestFactory requestFactory) {
return RestTemplateBuilder.builder().endpointProvider(endpointProvider)
.requestFactory(requestFactory);
}
/**
* Create a {@link VaultTemplate}.
*
@@ -85,8 +103,8 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw
*/
@Bean
public VaultTemplate vaultTemplate() {
return new VaultTemplate(vaultEndpointProvider(),
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory(),
return new VaultTemplate(restTemplateBuilder(vaultEndpointProvider(),
clientHttpRequestFactoryWrapper().getClientHttpRequestFactory()),
sessionManager());
}

View File

@@ -15,29 +15,22 @@
*/
package org.springframework.vault.config;
import java.io.IOException;
import java.security.GeneralSecurityException;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
import reactor.netty.http.client.HttpClient;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import static org.springframework.vault.config.ClientHttpRequestFactoryFactory.createKeyManagerFactory;
import static org.springframework.vault.config.ClientHttpRequestFactoryFactory.createTrustManagerFactory;
import static org.springframework.vault.config.ClientHttpRequestFactoryFactory.hasSslConfiguration;
/**
* Factory for {@link ClientHttpConnector} that supports
* {@link ReactorClientHttpConnector}.
*
* @author Mark Paluch
* @since 2.0
* @deprecated since 2.2, use
* {@link org.springframework.vault.client.ClientHttpConnectorFactory} as the
* functionality was moved to the {@code org.springframework.vault.client} package.
*/
@Deprecated
public class ClientHttpConnectorFactory {
/**
@@ -50,44 +43,7 @@ public class ClientHttpConnectorFactory {
*/
public static ClientHttpConnector create(ClientOptions options,
SslConfiguration sslConfiguration) {
HttpClient client = HttpClient.create();
if (hasSslConfiguration(sslConfiguration)) {
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
configureSsl(sslConfiguration, sslContextBuilder);
client = client.secure(builder -> {
builder.sslContext(sslContextBuilder);
});
}
client = client.tcpConfiguration(it -> it.option(
ChannelOption.CONNECT_TIMEOUT_MILLIS,
Math.toIntExact(options.getConnectionTimeout().toMillis())));
return new ReactorClientHttpConnector(client);
}
private static void configureSsl(SslConfiguration sslConfiguration,
SslContextBuilder sslContextBuilder) {
try {
if (sslConfiguration.getTrustStoreConfiguration().isPresent()) {
sslContextBuilder.trustManager(createTrustManagerFactory(sslConfiguration
.getTrustStoreConfiguration()));
}
if (sslConfiguration.getKeyStoreConfiguration().isPresent()) {
sslContextBuilder.keyManager(createKeyManagerFactory(
sslConfiguration.getKeyStoreConfiguration(),
sslConfiguration.getKeyConfiguration()));
}
}
catch (GeneralSecurityException | IOException e) {
throw new IllegalStateException(e);
}
return org.springframework.vault.client.ClientHttpConnectorFactory.create(
options, sslConfiguration);
}
}

View File

@@ -15,60 +15,9 @@
*/
package org.springframework.vault.config;
import java.io.IOException;
import java.io.InputStream;
import java.net.ProxySelector;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import okhttp3.OkHttpClient.Builder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.DefaultSchemePortResolver;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.support.SslConfiguration.KeyStoreConfiguration;
import static org.springframework.vault.support.SslConfiguration.KeyConfiguration;
/**
* Factory for {@link ClientHttpRequestFactory} that supports Apache HTTP Components,
@@ -76,39 +25,13 @@ import static org.springframework.vault.support.SslConfiguration.KeyConfiguratio
* {@link ClientHttpRequestFactory} depending on the available dependencies.
*
* @author Mark Paluch
* @deprecated since 2.2, use
* {@link org.springframework.vault.client.ClientHttpRequestFactoryFactory} as the
* functionality was moved to the {@code org.springframework.vault.client} package.
*/
@Deprecated
public class ClientHttpRequestFactoryFactory {
private static final Log logger = LogFactory
.getLog(ClientHttpRequestFactoryFactory.class);
private static final boolean HTTP_COMPONENTS_PRESENT = isPresent("org.apache.http.client.HttpClient");
private static final boolean OKHTTP3_PRESENT = isPresent("okhttp3.OkHttpClient");
private static final boolean NETTY_PRESENT = isPresent(
"io.netty.channel.nio.NioEventLoopGroup", "io.netty.handler.ssl.SslContext",
"io.netty.handler.codec.http.HttpClientCodec");
/**
* Checks for presence of all {@code classNames} using this class' classloader.
*
* @param classNames
* @return {@literal true} if all classes are present; {@literal false} if at least
* one class cannot be found.
*/
private static boolean isPresent(String... classNames) {
for (String className : classNames) {
if (!ClassUtils.isPresent(className,
ClientHttpRequestFactoryFactory.class.getClassLoader())) {
return false;
}
}
return true;
}
/**
* Create a {@link ClientHttpRequestFactory} for the given {@link ClientOptions} and
* {@link SslConfiguration}.
@@ -120,336 +43,7 @@ public class ClientHttpRequestFactoryFactory {
*/
public static ClientHttpRequestFactory create(ClientOptions options,
SslConfiguration sslConfiguration) {
Assert.notNull(options, "ClientOptions must not be null");
Assert.notNull(sslConfiguration, "SslConfiguration must not be null");
try {
if (HTTP_COMPONENTS_PRESENT) {
return HttpComponents.usingHttpComponents(options, sslConfiguration);
}
if (OKHTTP3_PRESENT) {
return OkHttp3.usingOkHttp3(options, sslConfiguration);
}
if (NETTY_PRESENT) {
return Netty.usingNetty(options, sslConfiguration);
}
}
catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
}
catch (IOException e) {
throw new IllegalStateException(e);
}
if (hasSslConfiguration(sslConfiguration)) {
logger.warn("VaultProperties has SSL configured but the SSL configuration "
+ "must be applied outside the Vault Client to use the JDK HTTP client");
}
return new SimpleClientHttpRequestFactory();
}
static SSLContext getSSLContext(SslConfiguration sslConfiguration,
TrustManager[] trustManagers) throws GeneralSecurityException, IOException {
KeyConfiguration keyConfiguration = sslConfiguration.getKeyConfiguration();
KeyManager[] keyManagers = sslConfiguration.getKeyStoreConfiguration()
.isPresent() ? createKeyManagerFactory(
sslConfiguration.getKeyStoreConfiguration(), keyConfiguration)
.getKeyManagers() : null;
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
private static TrustManager[] getTrustManagers(SslConfiguration sslConfiguration)
throws GeneralSecurityException, IOException {
return sslConfiguration.getTrustStoreConfiguration().isPresent() ? createTrustManagerFactory(
sslConfiguration.getTrustStoreConfiguration()).getTrustManagers()
: null;
}
static KeyManagerFactory createKeyManagerFactory(
KeyStoreConfiguration keyStoreConfiguration, KeyConfiguration keyConfiguration)
throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(StringUtils
.hasText(keyStoreConfiguration.getStoreType()) ? keyStoreConfiguration
.getStoreType() : KeyStore.getDefaultType());
loadKeyStore(keyStoreConfiguration, keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
char[] keyPasswordToUse = keyConfiguration.getKeyPassword();
if (keyPasswordToUse == null) {
keyPasswordToUse = keyStoreConfiguration.getStorePassword() == null ? new char[0]
: keyStoreConfiguration.getStorePassword();
}
keyManagerFactory.init(keyStore, keyPasswordToUse);
if (StringUtils.hasText(keyConfiguration.getKeyAlias())) {
return new KeySelectingKeyManagerFactory(keyManagerFactory, keyConfiguration);
}
return keyManagerFactory;
}
static TrustManagerFactory createTrustManagerFactory(
KeyStoreConfiguration keyStoreConfiguration) throws GeneralSecurityException,
IOException {
KeyStore trustStore = KeyStore.getInstance(StringUtils
.hasText(keyStoreConfiguration.getStoreType()) ? keyStoreConfiguration
.getStoreType() : KeyStore.getDefaultType());
loadKeyStore(keyStoreConfiguration, trustStore);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
return trustManagerFactory;
}
private static void loadKeyStore(KeyStoreConfiguration keyStoreConfiguration,
KeyStore keyStore) throws IOException, NoSuchAlgorithmException,
CertificateException {
InputStream inputStream = null;
try {
inputStream = keyStoreConfiguration.getResource().getInputStream();
keyStore.load(inputStream, keyStoreConfiguration.getStorePassword());
}
finally {
if (inputStream != null) {
inputStream.close();
}
}
}
static boolean hasSslConfiguration(SslConfiguration sslConfiguration) {
return sslConfiguration.getTrustStoreConfiguration().isPresent()
|| sslConfiguration.getKeyStoreConfiguration().isPresent();
}
/**
* {@link ClientHttpRequestFactory} for Apache Http Components.
*
* @author Mark Paluch
*/
static class HttpComponents {
static ClientHttpRequestFactory usingHttpComponents(ClientOptions options,
SslConfiguration sslConfiguration) throws GeneralSecurityException,
IOException {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(
DefaultSchemePortResolver.INSTANCE, ProxySelector.getDefault()));
if (hasSslConfiguration(sslConfiguration)) {
SSLContext sslContext = getSSLContext(sslConfiguration,
getTrustManagers(sslConfiguration));
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext);
httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
httpClientBuilder.setSSLContext(sslContext);
}
RequestConfig requestConfig = RequestConfig
.custom()
//
.setConnectTimeout(
Math.toIntExact(options.getConnectionTimeout().toMillis())) //
.setSocketTimeout(
Math.toIntExact(options.getReadTimeout().toMillis())) //
.setAuthenticationEnabled(true) //
.build();
httpClientBuilder.setDefaultRequestConfig(requestConfig);
// Support redirects
httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
}
}
/**
* {@link ClientHttpRequestFactory} for the {@link okhttp3.OkHttpClient}.
*
* @author Mark Paluch
*/
static class OkHttp3 {
static ClientHttpRequestFactory usingOkHttp3(ClientOptions options,
SslConfiguration sslConfiguration) throws GeneralSecurityException,
IOException {
Builder builder = new Builder();
if (hasSslConfiguration(sslConfiguration)) {
TrustManager[] trustManagers = getTrustManagers(sslConfiguration);
if (trustManagers.length != 1
|| !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
SSLContext sslContext = getSSLContext(sslConfiguration, trustManagers);
builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
}
builder.connectTimeout(options.getConnectionTimeout().toMillis(),
TimeUnit.MILLISECONDS).readTimeout(
options.getReadTimeout().toMillis(), TimeUnit.MILLISECONDS);
return new OkHttp3ClientHttpRequestFactory(builder.build());
}
}
/**
* {@link ClientHttpRequestFactory} for Netty.
*
* @author Mark Paluch
*/
static class Netty {
static ClientHttpRequestFactory usingNetty(ClientOptions options,
SslConfiguration sslConfiguration) throws GeneralSecurityException,
IOException {
final Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
if (hasSslConfiguration(sslConfiguration)) {
SslContextBuilder sslContextBuilder = SslContextBuilder //
.forClient();
if (sslConfiguration.getTrustStoreConfiguration().isPresent()) {
sslContextBuilder
.trustManager(createTrustManagerFactory(sslConfiguration
.getTrustStoreConfiguration()));
}
if (sslConfiguration.getKeyStoreConfiguration().isPresent()) {
sslContextBuilder.keyManager(createKeyManagerFactory(
sslConfiguration.getKeyStoreConfiguration(),
sslConfiguration.getKeyConfiguration()));
}
requestFactory.setSslContext(sslContextBuilder.sslProvider(
SslProvider.JDK).build());
}
requestFactory.setConnectTimeout(Math.toIntExact(options
.getConnectionTimeout().toMillis()));
requestFactory.setReadTimeout(Math.toIntExact(options.getReadTimeout()
.toMillis()));
return requestFactory;
}
}
static class KeySelectingKeyManagerFactory extends KeyManagerFactory {
KeySelectingKeyManagerFactory(KeyManagerFactory factory,
KeyConfiguration keyConfiguration) {
super(new KeyManagerFactorySpi() {
@Override
protected void engineInit(KeyStore keyStore, char[] chars)
throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException {
factory.init(keyStore, chars);
}
@Override
protected void engineInit(
ManagerFactoryParameters managerFactoryParameters)
throws InvalidAlgorithmParameterException {
factory.init(managerFactoryParameters);
}
@Override
protected KeyManager[] engineGetKeyManagers() {
KeyManager[] keyManagers = factory.getKeyManagers();
if (keyManagers.length == 1
&& keyManagers[0] instanceof X509ExtendedKeyManager) {
return new KeyManager[] { new KeySelectingX509KeyManager(
(X509ExtendedKeyManager) keyManagers[0], keyConfiguration) };
}
return keyManagers;
}
}, factory.getProvider(), factory.getAlgorithm());
}
}
private static class KeySelectingX509KeyManager extends X509ExtendedKeyManager {
private final X509ExtendedKeyManager delegate;
private final KeyConfiguration keyConfiguration;
KeySelectingX509KeyManager(X509ExtendedKeyManager delegate,
KeyConfiguration keyConfiguration) {
this.delegate = delegate;
this.keyConfiguration = keyConfiguration;
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return delegate.getClientAliases(keyType, issuers);
}
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket) {
return keyConfiguration.getKeyAlias();
}
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
SSLEngine engine) {
return keyConfiguration.getKeyAlias();
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return delegate.getServerAliases(keyType, issuers);
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return delegate.chooseServerAlias(keyType, issuers, socket);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return delegate.getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(String alias) {
return delegate.getPrivateKey(alias);
}
return org.springframework.vault.client.ClientHttpRequestFactoryFactory.create(
options, sslConfiguration);
}
}

View File

@@ -31,12 +31,12 @@ import org.springframework.util.Assert;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.VaultTokenSupplier;
import org.springframework.vault.client.ReactiveVaultClients;
import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultEndpointProvider;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.client.WebClientBuilder;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultResponseSupport;
import org.springframework.web.client.HttpStatusCodeException;
@@ -92,13 +92,33 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
Assert.notNull(vaultTokenSupplier, "AuthenticationSupplier must not be null");
Assert.notNull(vaultTokenSupplier, "VaultTokenSupplier must not be null");
this.vaultTokenSupplier = vaultTokenSupplier;
this.statelessClient = doCreateWebClient(endpointProvider, connector);
this.sessionClient = doCreateSessionWebClient(endpointProvider, connector);
}
/**
* Create a new {@link ReactiveVaultTemplate} through a {@link WebClientBuilder}, and
* {@link VaultTokenSupplier}.
*
* @param webClientBuilder must not be {@literal null}.
* @param vaultTokenSupplier must not be {@literal null}
* @since 2.2
*/
public ReactiveVaultTemplate(WebClientBuilder webClientBuilder,
VaultTokenSupplier vaultTokenSupplier) {
Assert.notNull(webClientBuilder, "WebClientBuilder must not be null");
Assert.notNull(vaultTokenSupplier, "VaultTokenSupplier must not be null");
this.vaultTokenSupplier = vaultTokenSupplier;
this.statelessClient = webClientBuilder.build();
this.sessionClient = webClientBuilder.build().mutate().filter(getSessionFilter())
.build();
}
/**
* Create a {@link WebClient} to be used by {@link ReactiveVaultTemplate} for Vault
* communication given {@link VaultEndpointProvider} and {@link ClientHttpConnector}.
@@ -117,7 +137,8 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
return ReactiveVaultClients.createWebClient(endpointProvider, connector);
return WebClientBuilder.builder().httpConnector(connector)
.endpointProvider(endpointProvider).build();
}
/**
@@ -139,16 +160,21 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(connector, "ClientHttpConnector must not be null");
ExchangeFilterFunction filter = ofRequestProcessor(request -> vaultTokenSupplier
.getVaultToken().map(token -> {
ExchangeFilterFunction filter = getSessionFilter();
return WebClientBuilder.builder().httpConnector(connector)
.endpointProvider(endpointProvider).filter(filter).build();
}
private ExchangeFilterFunction getSessionFilter() {
return ofRequestProcessor(request -> vaultTokenSupplier.getVaultToken().map(
token -> {
return ClientRequest.from(request).headers(headers -> {
headers.set(VaultHttpHeaders.VAULT_TOKEN, token.getToken());
}).build();
}));
return doCreateWebClient(endpointProvider, connector).mutate().filter(filter)
.build();
}
@Override

View File

@@ -25,14 +25,15 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.client.RestTemplateBuilder;
import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultEndpointProvider;
import org.springframework.vault.client.VaultHttpHeaders;
@@ -124,6 +125,28 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
this.sessionTemplate = doCreateSessionTemplate(endpointProvider, requestFactory);
}
/**
* Create a new {@link VaultTemplate} through a {@link RestTemplateBuilder} and
* {@link SessionManager}.
*
* @param restTemplateBuilder must not be {@literal null}.
* @param sessionManager must not be {@literal null}.
* @since 2.2
*/
public VaultTemplate(RestTemplateBuilder restTemplateBuilder,
SessionManager sessionManager) {
Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null");
Assert.notNull(sessionManager, "SessionManager must not be null");
this.sessionManager = sessionManager;
this.dedicatedSessionManager = false;
this.statelessTemplate = restTemplateBuilder.build();
this.sessionTemplate = restTemplateBuilder.build();
this.sessionTemplate.getInterceptors().add(getSessionInterceptor());
}
/**
* Create a {@link RestTemplate} to be used by {@link VaultTemplate} for Vault
* communication given {@link VaultEndpointProvider} and
@@ -139,10 +162,8 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
protected RestTemplate doCreateRestTemplate(VaultEndpointProvider endpointProvider,
ClientHttpRequestFactory requestFactory) {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
return VaultClients.createRestTemplate(endpointProvider, requestFactory);
return RestTemplateBuilder.builder().endpointProvider(endpointProvider)
.requestFactory(requestFactory).build();
}
/**
@@ -162,23 +183,26 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
VaultEndpointProvider endpointProvider,
ClientHttpRequestFactory requestFactory) {
Assert.notNull(endpointProvider, "VaultEndpointProvider must not be null");
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
return RestTemplateBuilder
.builder()
.endpointProvider(endpointProvider)
.requestFactory(requestFactory)
.customizer(
restTemplate -> restTemplate.getInterceptors().add(
getSessionInterceptor())).build();
}
RestTemplate restTemplate = doCreateRestTemplate(endpointProvider, requestFactory);
private ClientHttpRequestInterceptor getSessionInterceptor() {
restTemplate.getInterceptors().add(
(request, body, execution) -> {
return (request, body, execution) -> {
Assert.notNull(sessionManager, "SessionManager must not be null");
Assert.notNull(sessionManager, "SessionManager must not be null");
request.getHeaders().add(VaultHttpHeaders.VAULT_TOKEN,
sessionManager.getSessionToken().getToken());
request.getHeaders().add(VaultHttpHeaders.VAULT_TOKEN,
sessionManager.getSessionToken().getToken());
return execution.execute(request, body);
});
return restTemplate;
return execution.execute(request, body);
};
}
/**

View File

@@ -19,8 +19,8 @@ import org.junit.Test;
import org.springframework.core.NestedRuntimeException;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.support.VaultToken;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.config;
package org.springframework.vault.client;
import org.junit.Test;
@@ -25,10 +25,9 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory.HttpComponents;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory.Netty;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory.OkHttp3;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory.HttpComponents;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory.Netty;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory.OkHttp3;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.util.Settings;
import org.springframework.web.client.HttpStatusCodeException;
@@ -95,8 +94,8 @@ public class ClientHttpRequestFactoryFactoryIntegrationTests {
// Uninitialized and sealed can cause status 500
try {
ResponseEntity<String> responseEntity = template.exchange(url, HttpMethod.GET,
null, String.class);
ResponseEntity<String> responseEntity = template.exchange(url,
HttpMethod.GET, null, String.class);
return responseEntity.getBody();
}
catch (HttpStatusCodeException e) {

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2019 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.vault.client;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link RestTemplateBuilder}.
*
* @author Mark Paluch
*/
public class RestTemplateBuilderUnitTests {
@Test
public void shouldApplyErrorHandler() {
ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
RestTemplate restTemplate = RestTemplateBuilder.builder()
.endpoint(VaultEndpoint.create("localhost", 8200))
.errorHandler(errorHandler).build();
assertThat(restTemplate.getErrorHandler()).isSameAs(errorHandler);
}
@Test
public void shouldApplyErrorHandlerViaCustomizer() {
ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
RestTemplate restTemplate = RestTemplateBuilder.builder()
.endpoint(VaultEndpoint.create("localhost", 8200))
.customizer(it -> it.setErrorHandler(errorHandler)).build();
assertThat(restTemplate.getErrorHandler()).isSameAs(errorHandler);
}
@Test
public void shouldApplyDefaultHeaders() throws IOException {
RestTemplate restTemplate = RestTemplateBuilder.builder()
.endpoint(VaultEndpoint.create("localhost", 8200))
.defaultHeader("header", "value").build();
restTemplate.getInterceptors().clear();
ClientHttpRequest request = restTemplate.getRequestFactory().createRequest(
URI.create("/"), HttpMethod.GET);
assertThat(request.getHeaders()).containsEntry("header",
Collections.singletonList("value"));
}
@Test
public void shouldApplyRequestCustomizers() throws IOException {
RestTemplate restTemplate = RestTemplateBuilder
.builder()
.endpoint(VaultEndpoint.create("localhost", 8200))
.requestCustomizers(
request -> request.getHeaders().add("header", "value")).build();
restTemplate.getInterceptors().clear();
ClientHttpRequest request = restTemplate.getRequestFactory().createRequest(
URI.create("/"), HttpMethod.GET);
assertThat(request.getHeaders()).containsEntry("header",
Collections.singletonList("value"));
}
}

View File

@@ -21,9 +21,9 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.client.RestTemplate;

View File

@@ -17,9 +17,9 @@ package org.springframework.vault.util;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.util.Assert;
import org.springframework.vault.client.ClientHttpConnectorFactory;
import org.springframework.vault.client.ReactiveVaultClients;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.ClientHttpConnectorFactory;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.reactive.function.client.WebClient;
@@ -46,8 +46,7 @@ public class TestWebClientFactory {
try {
ClientHttpConnector connector = ClientHttpConnectorFactory.create(
new ClientOptions(), sslConfiguration);
return ReactiveVaultClients.createWebClient(TEST_VAULT_ENDPOINT,
connector);
return ReactiveVaultClients.createWebClient(TEST_VAULT_ENDPOINT, connector);
}
catch (Exception e) {
throw new IllegalStateException(e);