Polish gh-2699 Updates HttpClientFactory for backwards compatibility
If a user has extended HttpClientFactory and doesn't use the new SslConfigurer, the original protected methods need to be there. Those methods are marked as deprecated.
This commit is contained in:
@@ -16,9 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
@@ -26,6 +35,9 @@ import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import reactor.netty.http.Http11SslContextSpec;
|
||||
import reactor.netty.http.Http2SslContextSpec;
|
||||
import reactor.netty.http.HttpProtocol;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.http.client.HttpResponseDecoderSpec;
|
||||
@@ -38,6 +50,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.DISABLED;
|
||||
@@ -64,7 +77,7 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
List<HttpClientCustomizer> customizers) {
|
||||
this.properties = properties;
|
||||
this.serverProperties = serverProperties;
|
||||
this.sslConfigurer = new HttpClientSslConfigurer(properties.getSsl(), serverProperties);
|
||||
this.sslConfigurer = null;
|
||||
this.customizers = customizers;
|
||||
}
|
||||
|
||||
@@ -116,31 +129,45 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
}
|
||||
|
||||
protected HttpClient configureSsl(HttpClient httpClient) {
|
||||
return sslConfigurer.configureSsl(httpClient);
|
||||
if (sslConfigurer != null) {
|
||||
return sslConfigurer.configureSsl(httpClient);
|
||||
}
|
||||
|
||||
HttpClientProperties.Ssl ssl = properties.getSsl();
|
||||
if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
|
||||
|| getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
|
||||
httpClient = httpClient.secure(sslContextSpec -> {
|
||||
// configure ssl
|
||||
configureSslContext(ssl, sslContextSpec);
|
||||
});
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void configureSslContext(HttpClientProperties.Ssl ssl, SslProvider.SslContextSpec sslContextSpec) {
|
||||
sslConfigurer.configureSslContext(ssl, sslContextSpec);
|
||||
}
|
||||
SslProvider.ProtocolSslContextSpec clientSslContext = (serverProperties.getHttp2().isEnabled())
|
||||
? Http2SslContextSpec.forClient() : Http11SslContextSpec.forClient();
|
||||
clientSslContext.configure(sslContextBuilder -> {
|
||||
X509Certificate[] trustedX509Certificates = getTrustedX509CertificatesForTrustManager();
|
||||
if (trustedX509Certificates.length > 0) {
|
||||
setTrustManager(sslContextBuilder, trustedX509Certificates);
|
||||
}
|
||||
else if (ssl.isUseInsecureTrustManager()) {
|
||||
setTrustManager(sslContextBuilder, InsecureTrustManagerFactory.INSTANCE);
|
||||
}
|
||||
|
||||
protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
|
||||
return sslConfigurer.getTrustedX509CertificatesForTrustManager();
|
||||
}
|
||||
try {
|
||||
sslContextBuilder.keyManager(getKeyManagerFactory());
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
protected KeyManagerFactory getKeyManagerFactory() {
|
||||
return sslConfigurer.getKeyManagerFactory();
|
||||
}
|
||||
|
||||
protected KeyStore createKeyStore() {
|
||||
return sslConfigurer.createKeyStore();
|
||||
}
|
||||
|
||||
protected void setTrustManager(SslContextBuilder sslContextBuilder, X509Certificate... trustedX509Certificates) {
|
||||
sslConfigurer.setTrustManager(sslContextBuilder, trustedX509Certificates);
|
||||
}
|
||||
|
||||
protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
|
||||
sslConfigurer.setTrustManager(sslContextBuilder, factory);
|
||||
sslContextSpec.sslContext(clientSslContext).handshakeTimeout(ssl.getHandshakeTimeout())
|
||||
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
|
||||
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
|
||||
}
|
||||
|
||||
private HttpClient applyCustomizers(HttpClient httpClient) {
|
||||
@@ -165,6 +192,88 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
|
||||
HttpClientProperties.Ssl ssl = properties.getSsl();
|
||||
|
||||
try {
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
ArrayList<Certificate> allCerts = new ArrayList<>();
|
||||
for (String trustedCert : ssl.getTrustedX509Certificates()) {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(trustedCert);
|
||||
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(url.openStream());
|
||||
allCerts.addAll(certs);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Could not load certificate '" + trustedCert + "'", e);
|
||||
}
|
||||
}
|
||||
return allCerts.toArray(new X509Certificate[allCerts.size()]);
|
||||
}
|
||||
catch (CertificateException e1) {
|
||||
throw new RuntimeException("Could not load CertificateFactory X.509", e1);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected KeyManagerFactory getKeyManagerFactory() {
|
||||
HttpClientProperties.Ssl ssl = properties.getSsl();
|
||||
try {
|
||||
if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0) {
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
char[] keyPassword = ssl.getKeyPassword() != null ? ssl.getKeyPassword().toCharArray() : null;
|
||||
|
||||
if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
||||
keyPassword = ssl.getKeyStorePassword().toCharArray();
|
||||
}
|
||||
|
||||
keyManagerFactory.init(this.createKeyStore(), keyPassword);
|
||||
|
||||
return keyManagerFactory;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected KeyStore createKeyStore() {
|
||||
HttpClientProperties.Ssl ssl = properties.getSsl();
|
||||
try {
|
||||
KeyStore store = ssl.getKeyStoreProvider() != null
|
||||
? KeyStore.getInstance(ssl.getKeyStoreType(), ssl.getKeyStoreProvider())
|
||||
: KeyStore.getInstance(ssl.getKeyStoreType());
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(ssl.getKeyStore());
|
||||
store.load(url.openStream(),
|
||||
ssl.getKeyStorePassword() != null ? ssl.getKeyStorePassword().toCharArray() : null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Could not load key store ' " + ssl.getKeyStore() + "'", e);
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
catch (KeyStoreException | NoSuchProviderException e) {
|
||||
throw new RuntimeException("Could not load KeyStore for given type and provider", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setTrustManager(SslContextBuilder sslContextBuilder, X509Certificate... trustedX509Certificates) {
|
||||
sslContextBuilder.trustManager(trustedX509Certificates);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
|
||||
sslContextBuilder.trustManager(factory);
|
||||
}
|
||||
|
||||
protected ProxyProvider.Builder configureProxyProvider(HttpClientProperties.Proxy proxy,
|
||||
ProxyProvider.TypeSpec proxySpec) {
|
||||
ProxyProvider.Builder builder = proxySpec.type(proxy.getType()).host(proxy.getHost());
|
||||
|
||||
@@ -18,9 +18,12 @@ package org.springframework.cloud.gateway.config;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
@@ -164,6 +167,28 @@ public class GatewayAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
public void nettyHttpClientNoSslConfigurerIsBackwardsCompatible() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
NoSslConfigurerCustomHttpClientFactoryConfig.class))
|
||||
.withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
NoSslConfigurerHttpClientFactory factory = context.getBean(NoSslConfigurerHttpClientFactory.class);
|
||||
|
||||
assertThat(factory.configureSslCalled).isTrue();
|
||||
assertThat(factory.configureSslContextCalled).isTrue();
|
||||
assertThat(factory.getTrustedX509CertificatesForTrustManagerCalled).isTrue();
|
||||
assertThat(factory.getKeyManagerFactoryCalled).isTrue();
|
||||
assertThat(factory.createKeyStoreCalled).isFalse();
|
||||
assertThat(factory.setTrustManagerCertCalled).isFalse();
|
||||
assertThat(factory.setTrustManagerFactoryCalled).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verboseActuatorEnabledByDefault() {
|
||||
try (ConfigurableApplicationContext ctx = SpringApplication.run(Config.class, "--spring.jmx.enabled=false",
|
||||
@@ -325,6 +350,21 @@ public class GatewayAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ServerProperties.class)
|
||||
@AutoConfigureBefore(GatewayAutoConfiguration.class)
|
||||
@Deprecated
|
||||
protected static class NoSslConfigurerCustomHttpClientFactoryConfig {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
NoSslConfigurerHttpClientFactory noSslConfigurerHttpClientFactory(HttpClientProperties properties,
|
||||
ServerProperties serverProperties, List<HttpClientCustomizer> customizers) {
|
||||
return new NoSslConfigurerHttpClientFactory(properties, serverProperties, customizers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class CustomHttpClientFactory extends HttpClientFactory {
|
||||
|
||||
private ConnectionProvider connectionProvider;
|
||||
@@ -387,6 +427,76 @@ public class GatewayAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Class to test backwards compatibility if no `SslConfigurer` used.
|
||||
*/
|
||||
@Deprecated
|
||||
protected static class NoSslConfigurerHttpClientFactory extends HttpClientFactory {
|
||||
|
||||
boolean configureSslCalled;
|
||||
|
||||
boolean configureSslContextCalled;
|
||||
|
||||
boolean getTrustedX509CertificatesForTrustManagerCalled;
|
||||
|
||||
boolean getKeyManagerFactoryCalled;
|
||||
|
||||
boolean createKeyStoreCalled;
|
||||
|
||||
boolean setTrustManagerCertCalled;
|
||||
|
||||
boolean setTrustManagerFactoryCalled;
|
||||
|
||||
public NoSslConfigurerHttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties,
|
||||
List<HttpClientCustomizer> customizers) {
|
||||
super(properties, serverProperties, customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpClient configureSsl(HttpClient httpClient) {
|
||||
configureSslCalled = true;
|
||||
return super.configureSsl(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSslContext(HttpClientProperties.Ssl ssl, SslProvider.SslContextSpec sslContextSpec) {
|
||||
configureSslContextCalled = true;
|
||||
super.configureSslContext(ssl, sslContextSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
|
||||
getTrustedX509CertificatesForTrustManagerCalled = true;
|
||||
return super.getTrustedX509CertificatesForTrustManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyManagerFactory getKeyManagerFactory() {
|
||||
getKeyManagerFactoryCalled = true;
|
||||
return super.getKeyManagerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyStore createKeyStore() {
|
||||
createKeyStoreCalled = true;
|
||||
return super.createKeyStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setTrustManager(SslContextBuilder sslContextBuilder,
|
||||
X509Certificate... trustedX509Certificates) {
|
||||
setTrustManagerCertCalled = true;
|
||||
super.setTrustManager(sslContextBuilder, trustedX509Certificates);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
|
||||
setTrustManagerFactoryCalled = true;
|
||||
super.setTrustManager(sslContextBuilder, factory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
protected static class Config {
|
||||
|
||||
Reference in New Issue
Block a user