mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Apply TLS configuration to reactive web servers
This commit applies the SSL configuration to the following reactive web servers: Jetty, Tomcat, Undertow, Reactor Netty. Closes gh-9431
This commit is contained in:
parent
5f57578b71
commit
7f58db7d0e
@ -97,11 +97,13 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||
ServletContextHandler contextHandler = new ServletContextHandler(server, "",
|
||||
false, false);
|
||||
contextHandler.addServlet(servletHolder, "/");
|
||||
this.logger.info("Server initialized with port: " + port);
|
||||
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(port,
|
||||
getSsl(), getSslStoreProvider());
|
||||
sslServerCustomizer.customize(server);
|
||||
for (JettyServerCustomizer customizer : getServerCustomizers()) {
|
||||
customizer.customize(server);
|
||||
}
|
||||
JettyReactiveWebServerFactory.logger
|
||||
.info("Server initialized with port: " + port);
|
||||
return server;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.AbstractConnector;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
@ -44,12 +43,9 @@ import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
|
||||
@ -62,7 +58,6 @@ import org.eclipse.jetty.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.util.resource.JarResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
@ -71,18 +66,13 @@ import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.springframework.boot.web.server.Compression;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.MimeMappings;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.Ssl.ClientAuth;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@ -163,13 +153,9 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||
configureWebAppContext(context, initializers);
|
||||
server.setHandler(addHandlerWrappers(context));
|
||||
this.logger.info("Server initialized with port: " + port);
|
||||
if (getSsl() != null && getSsl().isEnabled()) {
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
configureSsl(sslContextFactory, getSsl());
|
||||
AbstractConnector connector = createSslConnector(server, sslContextFactory,
|
||||
port);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
}
|
||||
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(port,
|
||||
getSsl(), getSslStoreProvider());
|
||||
sslServerCustomizer.customize(server);
|
||||
for (JettyServerCustomizer customizer : getServerCustomizers()) {
|
||||
customizer.customize(server);
|
||||
}
|
||||
@ -181,7 +167,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||
|
||||
private Server createServer(InetSocketAddress address) {
|
||||
Server server = new Server(getThreadPool());
|
||||
server.setConnectors(new Connector[] { createConnector(address, server) });
|
||||
server.setConnectors(new Connector[] {createConnector(address, server)});
|
||||
return server;
|
||||
}
|
||||
|
||||
@ -199,20 +185,6 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||
return connector;
|
||||
}
|
||||
|
||||
private AbstractConnector createSslConnector(Server server,
|
||||
SslContextFactory sslContextFactory, int port) {
|
||||
HttpConfiguration config = new HttpConfiguration();
|
||||
config.setSendServerVersion(false);
|
||||
config.addCustomizer(new SecureRequestCustomizer());
|
||||
HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config);
|
||||
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(
|
||||
sslContextFactory, HttpVersion.HTTP_1_1.asString());
|
||||
ServerConnector serverConnector = new ServerConnector(server,
|
||||
sslConnectionFactory, connectionFactory);
|
||||
serverConnector.setPort(port);
|
||||
return serverConnector;
|
||||
}
|
||||
|
||||
private Handler addHandlerWrappers(Handler handler) {
|
||||
if (getCompression() != null && getCompression().getEnabled()) {
|
||||
handler = applyWrapper(handler, createGzipHandler());
|
||||
@ -242,96 +214,6 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the SSL connection.
|
||||
* @param factory the Jetty {@link SslContextFactory}.
|
||||
* @param ssl the ssl details.
|
||||
*/
|
||||
protected void configureSsl(SslContextFactory factory, Ssl ssl) {
|
||||
factory.setProtocol(ssl.getProtocol());
|
||||
configureSslClientAuth(factory, ssl);
|
||||
configureSslPasswords(factory, ssl);
|
||||
factory.setCertAlias(ssl.getKeyAlias());
|
||||
if (!ObjectUtils.isEmpty(ssl.getCiphers())) {
|
||||
factory.setIncludeCipherSuites(ssl.getCiphers());
|
||||
factory.setExcludeCipherSuites();
|
||||
}
|
||||
if (ssl.getEnabledProtocols() != null) {
|
||||
factory.setIncludeProtocols(ssl.getEnabledProtocols());
|
||||
}
|
||||
if (getSslStoreProvider() != null) {
|
||||
try {
|
||||
factory.setKeyStore(getSslStoreProvider().getKeyStore());
|
||||
factory.setTrustStore(getSslStoreProvider().getTrustStore());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to set SSL store", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
configureSslKeyStore(factory, ssl);
|
||||
configureSslTrustStore(factory, ssl);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslClientAuth(SslContextFactory factory, Ssl ssl) {
|
||||
if (ssl.getClientAuth() == ClientAuth.NEED) {
|
||||
factory.setNeedClientAuth(true);
|
||||
factory.setWantClientAuth(true);
|
||||
}
|
||||
else if (ssl.getClientAuth() == ClientAuth.WANT) {
|
||||
factory.setWantClientAuth(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslPasswords(SslContextFactory factory, Ssl ssl) {
|
||||
if (ssl.getKeyStorePassword() != null) {
|
||||
factory.setKeyStorePassword(ssl.getKeyStorePassword());
|
||||
}
|
||||
if (ssl.getKeyPassword() != null) {
|
||||
factory.setKeyManagerPassword(ssl.getKeyPassword());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslKeyStore(SslContextFactory factory, Ssl ssl) {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(ssl.getKeyStore());
|
||||
factory.setKeyStoreResource(Resource.newResource(url));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new WebServerException(
|
||||
"Could not find key store '" + ssl.getKeyStore() + "'", ex);
|
||||
}
|
||||
if (ssl.getKeyStoreType() != null) {
|
||||
factory.setKeyStoreType(ssl.getKeyStoreType());
|
||||
}
|
||||
if (ssl.getKeyStoreProvider() != null) {
|
||||
factory.setKeyStoreProvider(ssl.getKeyStoreProvider());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslTrustStore(SslContextFactory factory, Ssl ssl) {
|
||||
if (ssl.getTrustStorePassword() != null) {
|
||||
factory.setTrustStorePassword(ssl.getTrustStorePassword());
|
||||
}
|
||||
if (ssl.getTrustStore() != null) {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(ssl.getTrustStore());
|
||||
factory.setTrustStoreResource(Resource.newResource(url));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new WebServerException(
|
||||
"Could not find trust store '" + ssl.getTrustStore() + "'", ex);
|
||||
}
|
||||
}
|
||||
if (ssl.getTrustStoreType() != null) {
|
||||
factory.setTrustStoreType(ssl.getTrustStoreType());
|
||||
}
|
||||
if (ssl.getTrustStoreProvider() != null) {
|
||||
factory.setTrustStoreProvider(ssl.getTrustStoreProvider());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the given Jetty {@link WebAppContext} for use.
|
||||
* @param context the context to configure
|
||||
@ -456,7 +338,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||
context.getServletHandler().addServlet(holder);
|
||||
ServletMapping mapping = new ServletMapping();
|
||||
mapping.setServletName("jsp");
|
||||
mapping.setPathSpecs(new String[] { "*.jsp", "*.jspx" });
|
||||
mapping.setPathSpecs(new String[] {"*.jsp", "*.jspx"});
|
||||
context.getServletHandler().addServletMapping(mapping);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.jetty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.server.AbstractConnector;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProvider;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* {@link JettyServerCustomizer} that configures SSL on the
|
||||
* given Jetty server instance.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class SslServerCustomizer implements JettyServerCustomizer {
|
||||
|
||||
private final int port;
|
||||
|
||||
private final Ssl ssl;
|
||||
|
||||
private final SslStoreProvider sslStoreProvider;
|
||||
|
||||
SslServerCustomizer(int port, Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
this.port = port;
|
||||
this.ssl = ssl;
|
||||
this.sslStoreProvider = sslStoreProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Server server) {
|
||||
if (this.ssl != null && this.ssl.isEnabled()) {
|
||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||
configureSsl(sslContextFactory, this.ssl, this.sslStoreProvider);
|
||||
AbstractConnector connector = createSslConnector(server, sslContextFactory,
|
||||
this.port);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractConnector createSslConnector(Server server,
|
||||
SslContextFactory sslContextFactory, int port) {
|
||||
HttpConfiguration config = new HttpConfiguration();
|
||||
config.setSendServerVersion(false);
|
||||
config.addCustomizer(new SecureRequestCustomizer());
|
||||
HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config);
|
||||
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(
|
||||
sslContextFactory, HttpVersion.HTTP_1_1.asString());
|
||||
ServerConnector serverConnector = new ServerConnector(server,
|
||||
sslConnectionFactory, connectionFactory);
|
||||
serverConnector.setPort(port);
|
||||
return serverConnector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the SSL connection.
|
||||
* @param factory the Jetty {@link SslContextFactory}.
|
||||
* @param ssl the ssl details.
|
||||
* @param sslStoreProvider the ssl store provider
|
||||
*/
|
||||
protected void configureSsl(SslContextFactory factory, Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
factory.setProtocol(ssl.getProtocol());
|
||||
configureSslClientAuth(factory, ssl);
|
||||
configureSslPasswords(factory, ssl);
|
||||
factory.setCertAlias(ssl.getKeyAlias());
|
||||
if (!ObjectUtils.isEmpty(ssl.getCiphers())) {
|
||||
factory.setIncludeCipherSuites(ssl.getCiphers());
|
||||
factory.setExcludeCipherSuites();
|
||||
}
|
||||
if (ssl.getEnabledProtocols() != null) {
|
||||
factory.setIncludeProtocols(ssl.getEnabledProtocols());
|
||||
}
|
||||
if (sslStoreProvider != null) {
|
||||
try {
|
||||
factory.setKeyStore(sslStoreProvider.getKeyStore());
|
||||
factory.setTrustStore(sslStoreProvider.getTrustStore());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to set SSL store", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
configureSslKeyStore(factory, ssl);
|
||||
configureSslTrustStore(factory, ssl);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslClientAuth(SslContextFactory factory, Ssl ssl) {
|
||||
if (ssl.getClientAuth() == Ssl.ClientAuth.NEED) {
|
||||
factory.setNeedClientAuth(true);
|
||||
factory.setWantClientAuth(true);
|
||||
}
|
||||
else if (ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
|
||||
factory.setWantClientAuth(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslPasswords(SslContextFactory factory, Ssl ssl) {
|
||||
if (ssl.getKeyStorePassword() != null) {
|
||||
factory.setKeyStorePassword(ssl.getKeyStorePassword());
|
||||
}
|
||||
if (ssl.getKeyPassword() != null) {
|
||||
factory.setKeyManagerPassword(ssl.getKeyPassword());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslKeyStore(SslContextFactory factory, Ssl ssl) {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(ssl.getKeyStore());
|
||||
factory.setKeyStoreResource(Resource.newResource(url));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new WebServerException(
|
||||
"Could not find key store '" + ssl.getKeyStore() + "'", ex);
|
||||
}
|
||||
if (ssl.getKeyStoreType() != null) {
|
||||
factory.setKeyStoreType(ssl.getKeyStoreType());
|
||||
}
|
||||
if (ssl.getKeyStoreProvider() != null) {
|
||||
factory.setKeyStoreProvider(ssl.getKeyStoreProvider());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslTrustStore(SslContextFactory factory, Ssl ssl) {
|
||||
if (ssl.getTrustStorePassword() != null) {
|
||||
factory.setTrustStorePassword(ssl.getTrustStorePassword());
|
||||
}
|
||||
if (ssl.getTrustStore() != null) {
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(ssl.getTrustStore());
|
||||
factory.setTrustStoreResource(Resource.newResource(url));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new WebServerException(
|
||||
"Could not find trust store '" + ssl.getTrustStore() + "'", ex);
|
||||
}
|
||||
}
|
||||
if (ssl.getTrustStoreType() != null) {
|
||||
factory.setTrustStoreType(ssl.getTrustStoreType());
|
||||
}
|
||||
if (ssl.getTrustStoreProvider() != null) {
|
||||
factory.setTrustStoreProvider(ssl.getTrustStoreProvider());
|
||||
}
|
||||
}
|
||||
}
|
@ -90,6 +90,10 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||
private HttpServer createHttpServer() {
|
||||
return HttpServer.builder().options((options) -> {
|
||||
options.listenAddress(getListenAddress());
|
||||
if (getSsl() != null && getSsl().isEnabled()) {
|
||||
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(), getSslStoreProvider());
|
||||
sslServerCustomizer.customize(options);
|
||||
}
|
||||
applyCustomizers(options);
|
||||
}).build();
|
||||
}
|
||||
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.netty;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import reactor.ipc.netty.http.server.HttpServerOptions;
|
||||
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProvider;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* {@link NettyServerCustomizer} that configures SSL for the
|
||||
* given Reactor Netty server instance.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class SslServerCustomizer implements NettyServerCustomizer {
|
||||
|
||||
private final Ssl ssl;
|
||||
|
||||
private final SslStoreProvider sslStoreProvider;
|
||||
|
||||
public SslServerCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
this.ssl = ssl;
|
||||
this.sslStoreProvider = sslStoreProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(HttpServerOptions.Builder builder) {
|
||||
SslContextBuilder sslBuilder = SslContextBuilder
|
||||
.forServer(getKeyManagerFactory(this.ssl, this.sslStoreProvider))
|
||||
.trustManager(getTrustManagerFactory(this.ssl, this.sslStoreProvider));
|
||||
if (this.ssl.getEnabledProtocols() != null) {
|
||||
sslBuilder.protocols(this.ssl.getEnabledProtocols());
|
||||
}
|
||||
if (this.ssl.getCiphers() != null) {
|
||||
sslBuilder = sslBuilder.ciphers(Arrays.asList(this.ssl.getCiphers()));
|
||||
}
|
||||
try {
|
||||
builder.sslContext(sslBuilder.build());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected KeyManagerFactory getKeyManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
try {
|
||||
KeyStore keyStore = getKeyStore(ssl, sslStoreProvider);
|
||||
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(keyStore, keyPassword);
|
||||
return keyManagerFactory;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore getKeyStore(Ssl ssl, SslStoreProvider sslStoreProvider) throws Exception {
|
||||
if (sslStoreProvider != null) {
|
||||
return sslStoreProvider.getKeyStore();
|
||||
}
|
||||
return loadKeyStore(ssl.getKeyStoreType(), ssl.getKeyStore(),
|
||||
ssl.getKeyStorePassword());
|
||||
}
|
||||
|
||||
protected TrustManagerFactory getTrustManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
try {
|
||||
KeyStore store = getTrustStore(ssl, sslStoreProvider);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(store);
|
||||
return trustManagerFactory;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore getTrustStore(Ssl ssl, SslStoreProvider sslStoreProvider) throws Exception {
|
||||
if (sslStoreProvider != null) {
|
||||
return sslStoreProvider.getTrustStore();
|
||||
}
|
||||
return loadKeyStore(ssl.getTrustStoreType(), ssl.getTrustStore(),
|
||||
ssl.getTrustStorePassword());
|
||||
}
|
||||
|
||||
private KeyStore loadKeyStore(String type, String resource, String password)
|
||||
throws Exception {
|
||||
type = (type == null ? "JKS" : type);
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
KeyStore store = KeyStore.getInstance(type);
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
store.load(url.openStream(), password == null ? null : password.toCharArray());
|
||||
return store;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.tomcat;
|
||||
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.coyote.ProtocolHandler;
|
||||
import org.apache.coyote.http11.AbstractHttp11Protocol;
|
||||
|
||||
import org.springframework.boot.web.server.Compression;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link TomcatConnectorCustomizer} that configures compression
|
||||
* support on the given Connector.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class CompressionConnectorCustomizer implements TomcatConnectorCustomizer {
|
||||
|
||||
|
||||
private final Compression compression;
|
||||
|
||||
CompressionConnectorCustomizer(Compression compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Connector connector) {
|
||||
if (this.compression != null && this.compression.getEnabled()) {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractHttp11Protocol) {
|
||||
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) handler;
|
||||
Compression compression = this.compression;
|
||||
protocol.setCompression("on");
|
||||
protocol.setCompressionMinSize(compression.getMinResponseSize());
|
||||
protocol.setCompressibleMimeType(
|
||||
StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes()));
|
||||
if (this.compression.getExcludedUserAgents() != null) {
|
||||
protocol.setNoCompressionUserAgents(
|
||||
StringUtils.arrayToCommaDelimitedString(
|
||||
this.compression.getExcludedUserAgents()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.tomcat;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
|
||||
import org.apache.coyote.ProtocolHandler;
|
||||
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
|
||||
import org.apache.coyote.http11.Http11NioProtocol;
|
||||
import org.apache.tomcat.util.net.SSLHostConfig;
|
||||
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProvider;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link TomcatConnectorCustomizer} that configures SSL support
|
||||
* on the given connector.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class SslConnectorCustomizer implements TomcatConnectorCustomizer {
|
||||
|
||||
private final Ssl ssl;
|
||||
|
||||
private final SslStoreProvider sslStoreProvider;
|
||||
|
||||
SslConnectorCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
this.ssl = ssl;
|
||||
this.sslStoreProvider = sslStoreProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Connector connector) {
|
||||
if (this.ssl != null && this.ssl.isEnabled()) {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
Assert.state(handler instanceof AbstractHttp11JsseProtocol,
|
||||
"To use SSL, the connector's protocol handler must be an "
|
||||
+ "AbstractHttp11JsseProtocol subclass");
|
||||
configureSsl((AbstractHttp11JsseProtocol<?>) handler,
|
||||
this.ssl, this.sslStoreProvider);
|
||||
connector.setScheme("https");
|
||||
connector.setSecure(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Tomcat's {@link AbstractHttp11JsseProtocol} for SSL.
|
||||
* @param protocol the protocol
|
||||
* @param ssl the ssl details
|
||||
* @param sslStoreProvider the ssl store provider
|
||||
*/
|
||||
protected void configureSsl(AbstractHttp11JsseProtocol<?> protocol,
|
||||
Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
protocol.setSSLEnabled(true);
|
||||
protocol.setSslProtocol(ssl.getProtocol());
|
||||
configureSslClientAuth(protocol, ssl);
|
||||
protocol.setKeystorePass(ssl.getKeyStorePassword());
|
||||
protocol.setKeyPass(ssl.getKeyPassword());
|
||||
protocol.setKeyAlias(ssl.getKeyAlias());
|
||||
String ciphers = StringUtils.arrayToCommaDelimitedString(ssl.getCiphers());
|
||||
if (StringUtils.hasText(ciphers)) {
|
||||
protocol.setCiphers(ciphers);
|
||||
}
|
||||
if (ssl.getEnabledProtocols() != null) {
|
||||
for (SSLHostConfig sslHostConfig : protocol.findSslHostConfigs()) {
|
||||
sslHostConfig.setProtocols(StringUtils
|
||||
.arrayToCommaDelimitedString(ssl.getEnabledProtocols()));
|
||||
}
|
||||
}
|
||||
if (sslStoreProvider != null) {
|
||||
configureSslStoreProvider(protocol, sslStoreProvider);
|
||||
}
|
||||
else {
|
||||
configureSslKeyStore(protocol, ssl);
|
||||
configureSslTrustStore(protocol, ssl);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslClientAuth(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
if (ssl.getClientAuth() == Ssl.ClientAuth.NEED) {
|
||||
protocol.setClientAuth(Boolean.TRUE.toString());
|
||||
}
|
||||
else if (ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
|
||||
protocol.setClientAuth("want");
|
||||
}
|
||||
}
|
||||
|
||||
protected void configureSslStoreProvider(AbstractHttp11JsseProtocol<?> protocol,
|
||||
SslStoreProvider sslStoreProvider) {
|
||||
Assert.isInstanceOf(Http11NioProtocol.class, protocol,
|
||||
"SslStoreProvider can only be used with Http11NioProtocol");
|
||||
TomcatURLStreamHandlerFactory instance = TomcatURLStreamHandlerFactory
|
||||
.getInstance();
|
||||
instance.addUserFactory(
|
||||
new SslStoreProviderUrlStreamHandlerFactory(sslStoreProvider));
|
||||
protocol.setKeystoreFile(
|
||||
SslStoreProviderUrlStreamHandlerFactory.KEY_STORE_URL);
|
||||
protocol.setTruststoreFile(
|
||||
SslStoreProviderUrlStreamHandlerFactory.TRUST_STORE_URL);
|
||||
}
|
||||
|
||||
private void configureSslKeyStore(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
try {
|
||||
protocol.setKeystoreFile(ResourceUtils.getURL(ssl.getKeyStore()).toString());
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
throw new WebServerException("Could not load key store: " + ex.getMessage(),
|
||||
ex);
|
||||
}
|
||||
if (ssl.getKeyStoreType() != null) {
|
||||
protocol.setKeystoreType(ssl.getKeyStoreType());
|
||||
}
|
||||
if (ssl.getKeyStoreProvider() != null) {
|
||||
protocol.setKeystoreProvider(ssl.getKeyStoreProvider());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslTrustStore(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
if (ssl.getTrustStore() != null) {
|
||||
try {
|
||||
protocol.setTruststoreFile(
|
||||
ResourceUtils.getURL(ssl.getTrustStore()).toString());
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
throw new WebServerException(
|
||||
"Could not load trust store: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
protocol.setTruststorePass(ssl.getTrustStorePassword());
|
||||
if (ssl.getTrustStoreType() != null) {
|
||||
protocol.setTruststoreType(ssl.getTrustStoreType());
|
||||
}
|
||||
if (ssl.getTrustStoreProvider() != null) {
|
||||
protocol.setTruststoreProvider(ssl.getTrustStoreProvider());
|
||||
}
|
||||
}
|
||||
}
|
@ -141,6 +141,10 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
|
||||
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
|
||||
// prematurely...
|
||||
connector.setProperty("bindOnInit", "false");
|
||||
TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider());
|
||||
ssl.customize(connector);
|
||||
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
|
||||
compression.customize(connector);
|
||||
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
|
||||
customizer.customize(connector);
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package org.springframework.boot.web.embedded.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
@ -59,22 +58,11 @@ import org.apache.catalina.util.LifecycleBase;
|
||||
import org.apache.catalina.webresources.AbstractResourceSet;
|
||||
import org.apache.catalina.webresources.EmptyResource;
|
||||
import org.apache.catalina.webresources.StandardRoot;
|
||||
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.coyote.ProtocolHandler;
|
||||
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
|
||||
import org.apache.coyote.http11.AbstractHttp11Protocol;
|
||||
import org.apache.coyote.http11.Http11NioProtocol;
|
||||
import org.apache.tomcat.util.net.SSLHostConfig;
|
||||
|
||||
import org.springframework.boot.web.server.Compression;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.MimeMappings;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.Ssl.ClientAuth;
|
||||
import org.springframework.boot.web.server.SslStoreProvider;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
@ -82,7 +70,6 @@ import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@ -310,12 +297,10 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
|
||||
// prematurely...
|
||||
connector.setProperty("bindOnInit", "false");
|
||||
|
||||
if (getSsl() != null && getSsl().isEnabled()) {
|
||||
customizeSsl(connector);
|
||||
}
|
||||
if (getCompression() != null && getCompression().getEnabled()) {
|
||||
customizeCompression(connector);
|
||||
}
|
||||
TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider());
|
||||
ssl.customize(connector);
|
||||
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
|
||||
compression.customize(connector);
|
||||
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
|
||||
customizer.customize(connector);
|
||||
}
|
||||
@ -327,123 +312,6 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeSsl(Connector connector) {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
Assert.state(handler instanceof AbstractHttp11JsseProtocol,
|
||||
"To use SSL, the connector's protocol handler must be an "
|
||||
+ "AbstractHttp11JsseProtocol subclass");
|
||||
configureSsl((AbstractHttp11JsseProtocol<?>) handler, getSsl());
|
||||
connector.setScheme("https");
|
||||
connector.setSecure(true);
|
||||
}
|
||||
|
||||
private void customizeCompression(Connector connector) {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractHttp11Protocol) {
|
||||
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) handler;
|
||||
Compression compression = getCompression();
|
||||
protocol.setCompression("on");
|
||||
protocol.setCompressionMinSize(compression.getMinResponseSize());
|
||||
protocol.setCompressibleMimeType(
|
||||
StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes()));
|
||||
if (getCompression().getExcludedUserAgents() != null) {
|
||||
protocol.setNoCompressionUserAgents(
|
||||
StringUtils.arrayToCommaDelimitedString(
|
||||
getCompression().getExcludedUserAgents()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Tomcat's {@link AbstractHttp11JsseProtocol} for SSL.
|
||||
* @param protocol the protocol
|
||||
* @param ssl the ssl details
|
||||
*/
|
||||
protected void configureSsl(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
protocol.setSSLEnabled(true);
|
||||
protocol.setSslProtocol(ssl.getProtocol());
|
||||
configureSslClientAuth(protocol, ssl);
|
||||
protocol.setKeystorePass(ssl.getKeyStorePassword());
|
||||
protocol.setKeyPass(ssl.getKeyPassword());
|
||||
protocol.setKeyAlias(ssl.getKeyAlias());
|
||||
String ciphers = StringUtils.arrayToCommaDelimitedString(ssl.getCiphers());
|
||||
if (StringUtils.hasText(ciphers)) {
|
||||
protocol.setCiphers(ciphers);
|
||||
}
|
||||
if (ssl.getEnabledProtocols() != null) {
|
||||
for (SSLHostConfig sslHostConfig : protocol.findSslHostConfigs()) {
|
||||
sslHostConfig.setProtocols(StringUtils
|
||||
.arrayToCommaDelimitedString(ssl.getEnabledProtocols()));
|
||||
}
|
||||
}
|
||||
if (getSslStoreProvider() != null) {
|
||||
TomcatURLStreamHandlerFactory instance = TomcatURLStreamHandlerFactory
|
||||
.getInstance();
|
||||
instance.addUserFactory(
|
||||
new SslStoreProviderUrlStreamHandlerFactory(getSslStoreProvider()));
|
||||
protocol.setKeystoreFile(
|
||||
SslStoreProviderUrlStreamHandlerFactory.KEY_STORE_URL);
|
||||
protocol.setTruststoreFile(
|
||||
SslStoreProviderUrlStreamHandlerFactory.TRUST_STORE_URL);
|
||||
}
|
||||
else {
|
||||
configureSslKeyStore(protocol, ssl);
|
||||
configureSslTrustStore(protocol, ssl);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslClientAuth(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
if (ssl.getClientAuth() == ClientAuth.NEED) {
|
||||
protocol.setClientAuth(Boolean.TRUE.toString());
|
||||
}
|
||||
else if (ssl.getClientAuth() == ClientAuth.WANT) {
|
||||
protocol.setClientAuth("want");
|
||||
}
|
||||
}
|
||||
|
||||
protected void configureSslStoreProvider(AbstractHttp11JsseProtocol<?> protocol,
|
||||
SslStoreProvider sslStoreProvider) {
|
||||
Assert.isInstanceOf(Http11NioProtocol.class, protocol,
|
||||
"SslStoreProvider can only be used with Http11NioProtocol");
|
||||
}
|
||||
|
||||
private void configureSslKeyStore(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
try {
|
||||
protocol.setKeystoreFile(ResourceUtils.getURL(ssl.getKeyStore()).toString());
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
throw new WebServerException("Could not load key store: " + ex.getMessage(),
|
||||
ex);
|
||||
}
|
||||
if (ssl.getKeyStoreType() != null) {
|
||||
protocol.setKeystoreType(ssl.getKeyStoreType());
|
||||
}
|
||||
if (ssl.getKeyStoreProvider() != null) {
|
||||
protocol.setKeystoreProvider(ssl.getKeyStoreProvider());
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSslTrustStore(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl) {
|
||||
|
||||
if (ssl.getTrustStore() != null) {
|
||||
try {
|
||||
protocol.setTruststoreFile(
|
||||
ResourceUtils.getURL(ssl.getTrustStore()).toString());
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
throw new WebServerException(
|
||||
"Could not load trust store: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
protocol.setTruststorePass(ssl.getTrustStorePassword());
|
||||
if (ssl.getTrustStoreType() != null) {
|
||||
protocol.setTruststoreType(ssl.getTrustStoreType());
|
||||
}
|
||||
if (ssl.getTrustStoreProvider() != null) {
|
||||
protocol.setTruststoreProvider(ssl.getTrustStoreProvider());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Tomcat {@link Context}.
|
||||
* @param context the Tomcat context
|
||||
|
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.undertow;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
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 io.undertow.Undertow;
|
||||
import org.xnio.Options;
|
||||
import org.xnio.Sequence;
|
||||
import org.xnio.SslClientAuthMode;
|
||||
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.SslStoreProvider;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* {@link UndertowBuilderCustomizer} that configures SSL
|
||||
* on the given builder instance.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class SslBuilderCustomizer implements UndertowBuilderCustomizer {
|
||||
|
||||
private final int port;
|
||||
|
||||
private final InetAddress address;
|
||||
|
||||
private final Ssl ssl;
|
||||
|
||||
private final SslStoreProvider sslStoreProvider;
|
||||
|
||||
SslBuilderCustomizer(int port, InetAddress address,
|
||||
Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
this.port = port;
|
||||
this.address = address;
|
||||
this.ssl = ssl;
|
||||
this.sslStoreProvider = sslStoreProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Undertow.Builder builder) {
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance(this.ssl.getProtocol());
|
||||
sslContext.init(getKeyManagers(this.ssl, this.sslStoreProvider),
|
||||
getTrustManagers(this.ssl, this.sslStoreProvider), null);
|
||||
builder.addHttpsListener(this.port, getListenAddress(this.address), sslContext);
|
||||
builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE,
|
||||
getSslClientAuthMode(this.ssl));
|
||||
if (this.ssl.getEnabledProtocols() != null) {
|
||||
builder.setSocketOption(Options.SSL_ENABLED_PROTOCOLS,
|
||||
Sequence.of(this.ssl.getEnabledProtocols()));
|
||||
}
|
||||
if (this.ssl.getCiphers() != null) {
|
||||
builder.setSocketOption(Options.SSL_ENABLED_CIPHER_SUITES,
|
||||
Sequence.of(this.ssl.getCiphers()));
|
||||
}
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
catch (KeyManagementException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getListenAddress(InetAddress address) {
|
||||
if (this.address == null) {
|
||||
return "0.0.0.0";
|
||||
}
|
||||
return this.address.getHostAddress();
|
||||
}
|
||||
|
||||
private SslClientAuthMode getSslClientAuthMode(Ssl ssl) {
|
||||
if (ssl.getClientAuth() == Ssl.ClientAuth.NEED) {
|
||||
return SslClientAuthMode.REQUIRED;
|
||||
}
|
||||
if (ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
|
||||
return SslClientAuthMode.REQUESTED;
|
||||
}
|
||||
return SslClientAuthMode.NOT_REQUESTED;
|
||||
}
|
||||
|
||||
private KeyManager[] getKeyManagers(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
try {
|
||||
KeyStore keyStore = getKeyStore(ssl, sslStoreProvider);
|
||||
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(keyStore, keyPassword);
|
||||
if (ssl.getKeyAlias() != null) {
|
||||
return getConfigurableAliasKeyManagers(ssl,
|
||||
keyManagerFactory.getKeyManagers());
|
||||
}
|
||||
return keyManagerFactory.getKeyManagers();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyManager[] getConfigurableAliasKeyManagers(Ssl ssl,
|
||||
KeyManager[] keyManagers) {
|
||||
for (int i = 0; i < keyManagers.length; i++) {
|
||||
if (keyManagers[i] instanceof X509ExtendedKeyManager) {
|
||||
keyManagers[i] = new ConfigurableAliasKeyManager(
|
||||
(X509ExtendedKeyManager) keyManagers[i], ssl.getKeyAlias());
|
||||
}
|
||||
}
|
||||
return keyManagers;
|
||||
}
|
||||
|
||||
private KeyStore getKeyStore(Ssl ssl, SslStoreProvider sslStoreProvider) throws Exception {
|
||||
if (sslStoreProvider != null) {
|
||||
return sslStoreProvider.getKeyStore();
|
||||
}
|
||||
return loadKeyStore(ssl.getKeyStoreType(), ssl.getKeyStore(),
|
||||
ssl.getKeyStorePassword());
|
||||
}
|
||||
|
||||
private TrustManager[] getTrustManagers(Ssl ssl, SslStoreProvider sslStoreProvider) {
|
||||
try {
|
||||
KeyStore store = getTrustStore(ssl, sslStoreProvider);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(store);
|
||||
return trustManagerFactory.getTrustManagers();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore getTrustStore(Ssl ssl, SslStoreProvider sslStoreProvider) throws Exception {
|
||||
if (sslStoreProvider != null) {
|
||||
return sslStoreProvider.getTrustStore();
|
||||
}
|
||||
return loadKeyStore(ssl.getTrustStoreType(), ssl.getTrustStore(),
|
||||
ssl.getTrustStorePassword());
|
||||
}
|
||||
|
||||
private KeyStore loadKeyStore(String type, String resource, String password)
|
||||
throws Exception {
|
||||
type = (type == null ? "JKS" : type);
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
KeyStore store = KeyStore.getInstance(type);
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
store.load(url.openStream(), password == null ? null : password.toCharArray());
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link X509ExtendedKeyManager} that supports custom alias configuration.
|
||||
*/
|
||||
private static class ConfigurableAliasKeyManager extends X509ExtendedKeyManager {
|
||||
|
||||
private final X509ExtendedKeyManager keyManager;
|
||||
|
||||
private final String alias;
|
||||
|
||||
ConfigurableAliasKeyManager(X509ExtendedKeyManager keyManager, String alias) {
|
||||
this.keyManager = keyManager;
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseEngineClientAlias(String[] strings, Principal[] principals,
|
||||
SSLEngine sslEngine) {
|
||||
return this.keyManager.chooseEngineClientAlias(strings, principals,
|
||||
sslEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseEngineServerAlias(String s, Principal[] principals,
|
||||
SSLEngine sslEngine) {
|
||||
if (this.alias == null) {
|
||||
return this.keyManager.chooseEngineServerAlias(s, principals, sslEngine);
|
||||
}
|
||||
return this.alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseClientAlias(String[] keyType, Principal[] issuers,
|
||||
Socket socket) {
|
||||
return this.keyManager.chooseClientAlias(keyType, issuers, socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseServerAlias(String keyType, Principal[] issuers,
|
||||
Socket socket) {
|
||||
return this.keyManager.chooseServerAlias(keyType, issuers, socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getCertificateChain(String alias) {
|
||||
return this.keyManager.getCertificateChain(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getClientAliases(String keyType, Principal[] issuers) {
|
||||
return this.keyManager.getClientAliases(keyType, issuers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey(String alias) {
|
||||
return this.keyManager.getPrivateKey(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getServerAliases(String keyType, Principal[] issuers) {
|
||||
return this.keyManager.getServerAliases(keyType, issuers);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -85,7 +85,14 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
|
||||
if (this.directBuffers != null) {
|
||||
builder.setDirectBuffers(this.directBuffers);
|
||||
}
|
||||
builder.addHttpListener(port, getListenAddress());
|
||||
if (getSsl() != null && getSsl().isEnabled()) {
|
||||
SslBuilderCustomizer sslBuilderCustomizer =
|
||||
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider());
|
||||
sslBuilderCustomizer.customize(builder);
|
||||
}
|
||||
else {
|
||||
builder.addHttpListener(port, getListenAddress());
|
||||
}
|
||||
for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
|
||||
customizer.customize(builder);
|
||||
}
|
||||
|
@ -19,15 +19,8 @@ package org.springframework.boot.web.embedded.undertow;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -37,13 +30,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
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.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
@ -70,15 +56,11 @@ import io.undertow.servlet.handlers.DefaultServlet;
|
||||
import io.undertow.servlet.util.ImmediateInstanceFactory;
|
||||
import org.xnio.OptionMap;
|
||||
import org.xnio.Options;
|
||||
import org.xnio.Sequence;
|
||||
import org.xnio.SslClientAuthMode;
|
||||
import org.xnio.Xnio;
|
||||
import org.xnio.XnioWorker;
|
||||
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.MimeMappings.Mapping;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.Ssl.ClientAuth;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||
@ -86,7 +68,6 @@ import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* {@link ServletWebServerFactory} that can be used to create
|
||||
@ -251,7 +232,9 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||
builder.setDirectBuffers(this.directBuffers);
|
||||
}
|
||||
if (getSsl() != null && getSsl().isEnabled()) {
|
||||
configureSsl(getSsl(), port, builder);
|
||||
SslBuilderCustomizer sslBuilderCustomizer =
|
||||
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider());
|
||||
sslBuilderCustomizer.customize(builder);
|
||||
}
|
||||
else {
|
||||
builder.addHttpListener(port, getListenAddress());
|
||||
@ -262,30 +245,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void configureSsl(Ssl ssl, int port, Builder builder) {
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance(ssl.getProtocol());
|
||||
sslContext.init(getKeyManagers(), getTrustManagers(), null);
|
||||
builder.addHttpsListener(port, getListenAddress(), sslContext);
|
||||
builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE,
|
||||
getSslClientAuthMode(ssl));
|
||||
if (ssl.getEnabledProtocols() != null) {
|
||||
builder.setSocketOption(Options.SSL_ENABLED_PROTOCOLS,
|
||||
Sequence.of(ssl.getEnabledProtocols()));
|
||||
}
|
||||
if (ssl.getCiphers() != null) {
|
||||
builder.setSocketOption(Options.SSL_ENABLED_CIPHER_SUITES,
|
||||
Sequence.of(ssl.getCiphers()));
|
||||
}
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
catch (KeyManagementException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getListenAddress() {
|
||||
if (getAddress() == null) {
|
||||
return "0.0.0.0";
|
||||
@ -293,93 +252,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||
return getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
private SslClientAuthMode getSslClientAuthMode(Ssl ssl) {
|
||||
if (ssl.getClientAuth() == ClientAuth.NEED) {
|
||||
return SslClientAuthMode.REQUIRED;
|
||||
}
|
||||
if (ssl.getClientAuth() == ClientAuth.WANT) {
|
||||
return SslClientAuthMode.REQUESTED;
|
||||
}
|
||||
return SslClientAuthMode.NOT_REQUESTED;
|
||||
}
|
||||
|
||||
private KeyManager[] getKeyManagers() {
|
||||
try {
|
||||
KeyStore keyStore = getKeyStore();
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
Ssl ssl = getSsl();
|
||||
char[] keyPassword = (ssl.getKeyPassword() != null
|
||||
? ssl.getKeyPassword().toCharArray() : null);
|
||||
if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
||||
keyPassword = ssl.getKeyStorePassword().toCharArray();
|
||||
}
|
||||
keyManagerFactory.init(keyStore, keyPassword);
|
||||
if (ssl.getKeyAlias() != null) {
|
||||
return getConfigurableAliasKeyManagers(ssl,
|
||||
keyManagerFactory.getKeyManagers());
|
||||
}
|
||||
return keyManagerFactory.getKeyManagers();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyManager[] getConfigurableAliasKeyManagers(Ssl ssl,
|
||||
KeyManager[] keyManagers) {
|
||||
for (int i = 0; i < keyManagers.length; i++) {
|
||||
if (keyManagers[i] instanceof X509ExtendedKeyManager) {
|
||||
keyManagers[i] = new ConfigurableAliasKeyManager(
|
||||
(X509ExtendedKeyManager) keyManagers[i], ssl.getKeyAlias());
|
||||
}
|
||||
}
|
||||
return keyManagers;
|
||||
}
|
||||
|
||||
private KeyStore getKeyStore() throws Exception {
|
||||
if (getSslStoreProvider() != null) {
|
||||
return getSslStoreProvider().getKeyStore();
|
||||
}
|
||||
Ssl ssl = getSsl();
|
||||
return loadKeyStore(ssl.getKeyStoreType(), ssl.getKeyStore(),
|
||||
ssl.getKeyStorePassword());
|
||||
}
|
||||
|
||||
private TrustManager[] getTrustManagers() {
|
||||
try {
|
||||
KeyStore store = getTrustStore();
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(store);
|
||||
return trustManagerFactory.getTrustManagers();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore getTrustStore() throws Exception {
|
||||
if (getSslStoreProvider() != null) {
|
||||
return getSslStoreProvider().getTrustStore();
|
||||
}
|
||||
Ssl ssl = getSsl();
|
||||
return loadKeyStore(ssl.getTrustStoreType(), ssl.getTrustStore(),
|
||||
ssl.getTrustStorePassword());
|
||||
}
|
||||
|
||||
private KeyStore loadKeyStore(String type, String resource, String password)
|
||||
throws Exception {
|
||||
type = (type == null ? "JKS" : type);
|
||||
if (resource == null) {
|
||||
return null;
|
||||
}
|
||||
KeyStore store = KeyStore.getInstance(type);
|
||||
URL url = ResourceUtils.getURL(resource);
|
||||
store.load(url.openStream(), password == null ? null : password.toCharArray());
|
||||
return store;
|
||||
}
|
||||
|
||||
private DeploymentManager createDeploymentManager(
|
||||
ServletContextInitializer... initializers) {
|
||||
DeploymentInfo deployment = Servlets.deployment();
|
||||
@ -726,69 +598,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link X509ExtendedKeyManager} that supports custom alias configuration.
|
||||
*/
|
||||
private static class ConfigurableAliasKeyManager extends X509ExtendedKeyManager {
|
||||
|
||||
private final X509ExtendedKeyManager keyManager;
|
||||
|
||||
private final String alias;
|
||||
|
||||
ConfigurableAliasKeyManager(X509ExtendedKeyManager keyManager, String alias) {
|
||||
this.keyManager = keyManager;
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseEngineClientAlias(String[] strings, Principal[] principals,
|
||||
SSLEngine sslEngine) {
|
||||
return this.keyManager.chooseEngineClientAlias(strings, principals,
|
||||
sslEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseEngineServerAlias(String s, Principal[] principals,
|
||||
SSLEngine sslEngine) {
|
||||
if (this.alias == null) {
|
||||
return this.keyManager.chooseEngineServerAlias(s, principals, sslEngine);
|
||||
}
|
||||
return this.alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseClientAlias(String[] keyType, Principal[] issuers,
|
||||
Socket socket) {
|
||||
return this.keyManager.chooseClientAlias(keyType, issuers, socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseServerAlias(String keyType, Principal[] issuers,
|
||||
Socket socket) {
|
||||
return this.keyManager.chooseServerAlias(keyType, issuers, socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getCertificateChain(String alias) {
|
||||
return this.keyManager.getCertificateChain(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getClientAliases(String keyType, Principal[] issuers) {
|
||||
return this.keyManager.getClientAliases(keyType, issuers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey(String alias) {
|
||||
return this.keyManager.getPrivateKey(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getServerAliases(String keyType, Principal[] issuers) {
|
||||
return this.keyManager.getServerAliases(keyType, issuers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class LoaderHidingResourceManager implements ResourceManager {
|
||||
|
||||
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.tomcat;
|
||||
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.tomcat.util.net.SSLHostConfig;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslConnectorCustomizer}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class SslConnectorCustomizerTests {
|
||||
|
||||
private Tomcat tomcat;
|
||||
|
||||
private Connector connector;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.tomcat = new Tomcat();
|
||||
this.connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
|
||||
this.connector.setPort(0);
|
||||
this.tomcat.setConnector(this.connector);
|
||||
}
|
||||
|
||||
@After
|
||||
public void stop() throws Exception {
|
||||
this.tomcat.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sslCiphersConfiguration() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("test.jks");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
ssl.setCiphers(new String[] {"ALPHA", "BRAVO", "CHARLIE"});
|
||||
|
||||
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
|
||||
Connector connector = this.tomcat.getConnector();
|
||||
customizer.customize(connector);
|
||||
this.tomcat.start();
|
||||
|
||||
SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler()
|
||||
.findSslHostConfigs();
|
||||
assertThat(sslHostConfigs[0].getCiphers()).isEqualTo("ALPHA:BRAVO:CHARLIE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sslEnabledMultipleProtocolsConfiguration() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
|
||||
ssl.setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
|
||||
ssl.setCiphers(new String[] {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO"});
|
||||
|
||||
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
|
||||
Connector connector = this.tomcat.getConnector();
|
||||
customizer.customize(connector);
|
||||
this.tomcat.start();
|
||||
|
||||
SSLHostConfig sslHostConfig = connector.getProtocolHandler()
|
||||
.findSslHostConfigs()[0];
|
||||
assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS");
|
||||
assertThat(sslHostConfig.getEnabledProtocols())
|
||||
.containsExactlyInAnyOrder("TLSv1.1", "TLSv1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sslEnabledProtocolsConfiguration() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
|
||||
ssl.setEnabledProtocols(new String[] {"TLSv1.2"});
|
||||
ssl.setCiphers(new String[] {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO"});
|
||||
|
||||
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
|
||||
Connector connector = this.tomcat.getConnector();
|
||||
customizer.customize(connector);
|
||||
this.tomcat.start();
|
||||
|
||||
SSLHostConfig sslHostConfig = connector.getProtocolHandler()
|
||||
.findSslHostConfigs()[0];
|
||||
assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS");
|
||||
assertThat(sslHostConfig.getEnabledProtocols()).containsExactly("TLSv1.2");
|
||||
}
|
||||
}
|
@ -43,14 +43,12 @@ import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.util.CharsetMapper;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.apache.jasper.servlet.JspServlet;
|
||||
import org.apache.tomcat.util.net.SSLHostConfig;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.boot.testsupport.rule.OutputCapture;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests;
|
||||
@ -265,63 +263,6 @@ public class TomcatServletWebServerFactoryTests
|
||||
assertThat(connector.getURIEncoding()).isEqualTo("UTF-8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sslCiphersConfiguration() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore("test.jks");
|
||||
ssl.setKeyStorePassword("secret");
|
||||
ssl.setCiphers(new String[] { "ALPHA", "BRAVO", "CHARLIE" });
|
||||
TomcatServletWebServerFactory factory = getFactory();
|
||||
factory.setSsl(ssl);
|
||||
|
||||
Tomcat tomcat = getTomcat(factory);
|
||||
Connector connector = ((TomcatWebServer) this.webServer).getServiceConnectors()
|
||||
.get(tomcat.getService())[0];
|
||||
SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler()
|
||||
.findSslHostConfigs();
|
||||
assertThat(sslHostConfigs[0].getCiphers()).isEqualTo("ALPHA:BRAVO:CHARLIE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sslEnabledMultipleProtocolsConfiguration() throws Exception {
|
||||
Ssl ssl = getSsl(null, "password", "src/test/resources/test.jks");
|
||||
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
|
||||
ssl.setCiphers(new String[] { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO" });
|
||||
|
||||
TomcatServletWebServerFactory factory = getFactory();
|
||||
factory.setSsl(ssl);
|
||||
|
||||
this.webServer = factory.getWebServer(sessionServletRegistration());
|
||||
this.webServer.start();
|
||||
Tomcat tomcat = ((TomcatWebServer) this.webServer).getTomcat();
|
||||
Connector connector = tomcat.getConnector();
|
||||
|
||||
SSLHostConfig sslHostConfig = connector.getProtocolHandler()
|
||||
.findSslHostConfigs()[0];
|
||||
assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS");
|
||||
assertThat(sslHostConfig.getEnabledProtocols())
|
||||
.containsExactlyInAnyOrder("TLSv1.1", "TLSv1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sslEnabledProtocolsConfiguration() throws Exception {
|
||||
Ssl ssl = getSsl(null, "password", "src/test/resources/test.jks");
|
||||
ssl.setEnabledProtocols(new String[] { "TLSv1.2" });
|
||||
ssl.setCiphers(new String[] { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO" });
|
||||
|
||||
TomcatServletWebServerFactory factory = getFactory();
|
||||
factory.setSsl(ssl);
|
||||
|
||||
this.webServer = factory.getWebServer(sessionServletRegistration());
|
||||
Tomcat tomcat = ((TomcatWebServer) this.webServer).getTomcat();
|
||||
this.webServer.start();
|
||||
Connector connector = tomcat.getConnector();
|
||||
SSLHostConfig sslHostConfig = connector.getProtocolHandler()
|
||||
.findSslHostConfigs()[0];
|
||||
assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS");
|
||||
assertThat(sslHostConfig.getEnabledProtocols()).containsExactly("TLSv1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void primaryConnectorPortClashThrowsIllegalStateException()
|
||||
throws InterruptedException, IOException {
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.boot.web.embedded.undertow;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SslBuilderCustomizer}
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class SslBuilderCustomizerTests {
|
||||
|
||||
@Test
|
||||
public void getKeyManagersWhenAliasIsNullShouldNotDecorate() throws Exception {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("src/test/resources/test.jks");
|
||||
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null);
|
||||
KeyManager[] keyManagers = ReflectionTestUtils.invokeMethod(customizer,
|
||||
"getKeyManagers", ssl, null);
|
||||
Class<?> name = Class.forName("org.springframework.boot.web.embedded.undertow"
|
||||
+ ".SslBuilderCustomizer$ConfigurableAliasKeyManager");
|
||||
assertThat(keyManagers[0]).isNotInstanceOf(name);
|
||||
}
|
||||
}
|
@ -28,7 +28,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
@ -43,7 +42,6 @@ import org.springframework.boot.testsupport.web.servlet.ExampleServlet;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.MimeMappings.Mapping;
|
||||
import org.springframework.boot.web.server.PortInUseException;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests;
|
||||
@ -239,18 +237,6 @@ public class UndertowServletWebServerFactoryTests
|
||||
new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA256" });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getKeyManagersWhenAliasIsNullShouldNotDecorate() throws Exception {
|
||||
UndertowServletWebServerFactory factory = getFactory();
|
||||
Ssl ssl = getSsl(null, "password", "src/test/resources/test.jks");
|
||||
factory.setSsl(ssl);
|
||||
KeyManager[] keyManagers = ReflectionTestUtils.invokeMethod(factory,
|
||||
"getKeyManagers");
|
||||
Class<?> name = Class.forName("org.springframework.boot.web.embedded.undertow"
|
||||
+ ".UndertowServletWebServerFactory$ConfigurableAliasKeyManager");
|
||||
assertThat(keyManagers[0]).isNotInstanceOf(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JspServlet getJspServlet() {
|
||||
return null; // Undertow does not support JSPs
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.web.reactive.server;
|
||||
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@ -24,9 +26,11 @@ import org.junit.rules.TemporaryFolder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.testsupport.rule.OutputCapture;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
@ -75,7 +79,7 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
||||
factory.setPort(specificPort);
|
||||
this.webServer = factory.getWebServer(new EchoHandler());
|
||||
this.webServer.start();
|
||||
Mono<String> result = WebClient.create("http://localhost:" + specificPort).post()
|
||||
Mono<String> result = getWebClient().build().post()
|
||||
.uri("/test").contentType(MediaType.TEXT_PLAIN)
|
||||
.body(BodyInserters.fromObject("Hello World")).exchange()
|
||||
.flatMap((response) -> response.bodyToMono(String.class));
|
||||
@ -83,8 +87,49 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
||||
assertThat(this.webServer.getPort()).isEqualTo(specificPort);
|
||||
}
|
||||
|
||||
protected WebClient getWebClient() {
|
||||
return WebClient.create("http://localhost:" + this.webServer.getPort());
|
||||
@Test
|
||||
public void basicSslFromClassPath() throws Exception {
|
||||
testBasicSslWithKeyStore("classpath:test.jks");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicSslFromFileSystem() throws Exception {
|
||||
testBasicSslWithKeyStore("src/test/resources/test.jks");
|
||||
}
|
||||
|
||||
|
||||
protected final void testBasicSslWithKeyStore(String keyStore) throws Exception {
|
||||
AbstractReactiveWebServerFactory factory = getFactory();
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setKeyStore(keyStore);
|
||||
ssl.setKeyPassword("password");
|
||||
factory.setSsl(ssl);
|
||||
this.webServer = factory.getWebServer(new EchoHandler());
|
||||
this.webServer.start();
|
||||
|
||||
ReactorClientHttpConnector connector = buildTrustAllSslConnector();
|
||||
WebClient client = WebClient.builder()
|
||||
.baseUrl("https://localhost:" + this.webServer.getPort())
|
||||
.clientConnector(connector).build();
|
||||
|
||||
Mono<String> result = client.post()
|
||||
.uri("/test").contentType(MediaType.TEXT_PLAIN)
|
||||
.body(BodyInserters.fromObject("Hello World")).exchange()
|
||||
.flatMap((response) -> response.bodyToMono(String.class));
|
||||
assertThat(result.block()).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
protected ReactorClientHttpConnector buildTrustAllSslConnector() {
|
||||
return new ReactorClientHttpConnector(options -> options
|
||||
.sslSupport(sslContextBuilder -> {
|
||||
sslContextBuilder
|
||||
.sslProvider(SslProvider.JDK)
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE);
|
||||
}));
|
||||
}
|
||||
|
||||
protected WebClient.Builder getWebClient() {
|
||||
return WebClient.builder().baseUrl("http://localhost:" + this.webServer.getPort());
|
||||
}
|
||||
|
||||
protected static class EchoHandler implements HttpHandler {
|
||||
|
Loading…
Reference in New Issue
Block a user