diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 1b23af54ca..f7641cacbe 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -308,8 +308,8 @@ test - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.apache.tomcat + tomcat-juli test diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 56d1ff334a..c277fc0af2 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -173,7 +173,7 @@ 1.4.0 1.3 2.1.0.RELEASE - 8.0.33 + 8.5.3 1.3.22.Final 1.7 2.0 @@ -1314,11 +1314,6 @@ tomcat-embed-jasper ${tomcat.version} - - org.apache.tomcat.embed - tomcat-embed-logging-juli - ${tomcat.version} - org.apache.tomcat.embed tomcat-embed-websocket @@ -1334,6 +1329,11 @@ tomcat-jsp-api ${tomcat.version} + + org.apache.tomcat + tomcat-juli + ${tomcat.version} + org.apache.velocity velocity diff --git a/spring-boot-devtools/pom.xml b/spring-boot-devtools/pom.xml index 69468f6515..72c68b82cf 100644 --- a/spring-boot-devtools/pom.xml +++ b/spring-boot-devtools/pom.xml @@ -118,8 +118,8 @@ test - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.apache.tomcat + tomcat-juli test diff --git a/spring-boot-docs/pom.xml b/spring-boot-docs/pom.xml index 440ff6b884..d0fc03f938 100644 --- a/spring-boot-docs/pom.xml +++ b/spring-boot-docs/pom.xml @@ -300,8 +300,8 @@ true - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.apache.tomcat + tomcat-juli true diff --git a/spring-boot-samples/README.adoc b/spring-boot-samples/README.adoc index 79cb240275..590a0c437f 100644 --- a/spring-boot-samples/README.adoc +++ b/spring-boot-samples/README.adoc @@ -193,7 +193,7 @@ The following sample applications are provided: | Embedded Tomcat | link:spring-boot-sample-tomcat-jsp[spring-boot-sample-tomcat-jsp] -| Web application that uses JSP templates with Tomcat 8 +| Web application that uses JSP templates with Tomcat | link:spring-boot-sample-tomcat-multi-connectors[spring-boot-sample-tomcat-multi-connectors] | Web application that uses Tomcat configured with multiple connectors @@ -204,6 +204,12 @@ The following sample applications are provided: | link:spring-boot-sample-tomcat7-jsp[spring-boot-sample-tomcat7-jsp] | Web application that uses JSP templates with Tomcat 7 +| link:spring-boot-sample-tomcat7-ssl[spring-boot-sample-tomcat7-ssl] +| Web application that uses Tomcat 7 configured with SSL + +| link:spring-boot-sample-tomcat80-ssl[spring-boot-sample-tomcat80-ssl] +| Web application that uses Tomcat 8.0 configured with SSL + | link:spring-boot-sample-traditional[spring-boot-sample-traditional] | Traditional WAR packaging (but also executable using `java -jar`) diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index fbe02fd085..95984aceb6 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -87,6 +87,8 @@ spring-boot-sample-tomcat-ssl spring-boot-sample-tomcat-multi-connectors spring-boot-sample-tomcat7-jsp + spring-boot-sample-tomcat7-ssl + spring-boot-sample-tomcat80-ssl spring-boot-sample-traditional spring-boot-sample-undertow spring-boot-sample-undertow-ssl diff --git a/spring-boot-samples/spring-boot-sample-tomcat-jsp/pom.xml b/spring-boot-samples/spring-boot-sample-tomcat-jsp/pom.xml index c09885658b..0fe62952f9 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat-jsp/pom.xml +++ b/spring-boot-samples/spring-boot-sample-tomcat-jsp/pom.xml @@ -19,6 +19,7 @@ ${basedir}/../.. / + 7.0.69 diff --git a/spring-boot-samples/spring-boot-sample-tomcat-ssl/pom.xml b/spring-boot-samples/spring-boot-sample-tomcat-ssl/pom.xml index c583d31868..ad94327532 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat-ssl/pom.xml +++ b/spring-boot-samples/spring-boot-sample-tomcat-ssl/pom.xml @@ -8,7 +8,7 @@ 1.4.0.BUILD-SNAPSHOT spring-boot-sample-tomcat-ssl - Spring Boot Tomcat Sample + Spring Boot Tomcat SSL Sample Spring Boot Tomcat SSL Sample http://projects.spring.io/spring-boot/ diff --git a/spring-boot-samples/spring-boot-sample-tomcat7-ssl/pom.xml b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/pom.xml new file mode 100644 index 0000000000..8796da6189 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.4.0.BUILD-SNAPSHOT + + spring-boot-sample-tomcat7-ssl + Spring Boot Tomcat 7 SSL Sample + Spring Boot Tomcat 7 SSL Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + 7.0.69 + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework + spring-webmvc + + + org.apache.httpcomponents + httpclient + + + org.springframework.boot + spring-boot-starter-test + test + + + org.yaml + snakeyaml + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/java/sample/tomcat/ssl/SampleTomcatSslApplication.java b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/java/sample/tomcat/ssl/SampleTomcatSslApplication.java new file mode 100644 index 0000000000..32d2e6ed77 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/java/sample/tomcat/ssl/SampleTomcatSslApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2015 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 sample.tomcat.ssl; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleTomcatSslApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleTomcatSslApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/java/sample/tomcat/ssl/web/SampleController.java b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/java/sample/tomcat/ssl/web/SampleController.java new file mode 100644 index 0000000000..7ca0d5ed01 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/java/sample/tomcat/ssl/web/SampleController.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2016 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 sample.tomcat.ssl.web; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class SampleController { + + @GetMapping("/") + @ResponseBody + public String helloWorld() { + return "Hello, world"; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/resources/application.properties new file mode 100644 index 0000000000..37199bfd25 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/resources/application.properties @@ -0,0 +1,4 @@ +server.port = 8443 +server.ssl.key-store = classpath:sample.jks +server.ssl.key-store-password = secret +server.ssl.key-password = password diff --git a/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/resources/sample.jks b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/resources/sample.jks new file mode 100644 index 0000000000..6aa9a28053 Binary files /dev/null and b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/main/resources/sample.jks differ diff --git a/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/test/java/sample/tomcat/ssl/SampleTomcatSslApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/test/java/sample/tomcat/ssl/SampleTomcatSslApplicationTests.java new file mode 100644 index 0000000000..f7b42c68a6 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat7-ssl/src/test/java/sample/tomcat/ssl/SampleTomcatSslApplicationTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2016 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 sample.tomcat.ssl; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@DirtiesContext +public class SampleTomcatSslApplicationTests { + + @LocalServerPort + private int port; + + @Test + public void testHome() throws Exception { + TestRestTemplate testRestTemplate = new TestRestTemplate(HttpClientOption.SSL); + ResponseEntity entity = testRestTemplate + .getForEntity("https://localhost:" + this.port, String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).isEqualTo("Hello, world"); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-tomcat80-ssl/pom.xml b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/pom.xml new file mode 100644 index 0000000000..4da6d54d88 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.4.0.BUILD-SNAPSHOT + + spring-boot-sample-tomcat-ssl + Spring Boot Tomcat 8.0 SSL Sample + Spring Boot Tomcat 8.0 SSL Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + 8.0.33 + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework + spring-webmvc + + + org.apache.httpcomponents + httpclient + + + org.springframework.boot + spring-boot-starter-test + test + + + org.yaml + snakeyaml + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/java/sample/tomcat/ssl/SampleTomcatSslApplication.java b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/java/sample/tomcat/ssl/SampleTomcatSslApplication.java new file mode 100644 index 0000000000..32d2e6ed77 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/java/sample/tomcat/ssl/SampleTomcatSslApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2015 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 sample.tomcat.ssl; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleTomcatSslApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleTomcatSslApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/java/sample/tomcat/ssl/web/SampleController.java b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/java/sample/tomcat/ssl/web/SampleController.java new file mode 100644 index 0000000000..7ca0d5ed01 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/java/sample/tomcat/ssl/web/SampleController.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2016 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 sample.tomcat.ssl.web; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class SampleController { + + @GetMapping("/") + @ResponseBody + public String helloWorld() { + return "Hello, world"; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/resources/application.properties new file mode 100644 index 0000000000..37199bfd25 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/resources/application.properties @@ -0,0 +1,4 @@ +server.port = 8443 +server.ssl.key-store = classpath:sample.jks +server.ssl.key-store-password = secret +server.ssl.key-password = password diff --git a/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/resources/sample.jks b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/resources/sample.jks new file mode 100644 index 0000000000..6aa9a28053 Binary files /dev/null and b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/main/resources/sample.jks differ diff --git a/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/test/java/sample/tomcat/ssl/SampleTomcatSslApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/test/java/sample/tomcat/ssl/SampleTomcatSslApplicationTests.java new file mode 100644 index 0000000000..f7b42c68a6 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-tomcat80-ssl/src/test/java/sample/tomcat/ssl/SampleTomcatSslApplicationTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2016 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 sample.tomcat.ssl; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@DirtiesContext +public class SampleTomcatSslApplicationTests { + + @LocalServerPort + private int port; + + @Test + public void testHome() throws Exception { + TestRestTemplate testRestTemplate = new TestRestTemplate(HttpClientOption.SSL); + ResponseEntity entity = testRestTemplate + .getForEntity("https://localhost:" + this.port, String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).isEqualTo("Hello, world"); + } + +} diff --git a/spring-boot-starters/spring-boot-starter-tomcat/pom.xml b/spring-boot-starters/spring-boot-starter-tomcat/pom.xml index 50ccbc4d84..7f8388079e 100644 --- a/spring-boot-starters/spring-boot-starter-tomcat/pom.xml +++ b/spring-boot-starters/spring-boot-starter-tomcat/pom.xml @@ -28,8 +28,8 @@ tomcat-embed-el - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.apache.tomcat + tomcat-juli org.apache.tomcat.embed diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml index ff648694f3..067563b234 100644 --- a/spring-boot-test/pom.xml +++ b/spring-boot-test/pom.xml @@ -112,8 +112,8 @@ test - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.apache.tomcat + tomcat-juli test diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 25648b9977..c90fe9e397 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -110,8 +110,8 @@ true - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.apache.tomcat + tomcat-juli true diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedJSSEImplementation.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedJSSEImplementation.java deleted file mode 100644 index 6681da0c4e..0000000000 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedJSSEImplementation.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2012-2016 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.context.embedded.tomcat; - -import java.io.IOException; -import java.security.KeyStore; - -import org.apache.tomcat.util.net.AbstractEndpoint; -import org.apache.tomcat.util.net.SSLUtil; -import org.apache.tomcat.util.net.ServerSocketFactory; -import org.apache.tomcat.util.net.jsse.JSSEImplementation; -import org.apache.tomcat.util.net.jsse.JSSESocketFactory; - -import org.springframework.boot.context.embedded.SslStoreProvider; - -/** - * {@link JSSEImplementation} for embedded Tomcat that supports {@link SslStoreProvider}. - * - * @author Phillip Webb - * @author Venil Noronha - * @since 1.4.0 - */ -public class TomcatEmbeddedJSSEImplementation extends JSSEImplementation { - - @Override - public ServerSocketFactory getServerSocketFactory(AbstractEndpoint endpoint) { - return new SocketFactory(endpoint); - } - - @Override - public SSLUtil getSSLUtil(AbstractEndpoint endpoint) { - return new SocketFactory(endpoint); - } - - /** - * {@link JSSESocketFactory} that supports {@link SslStoreProvider}. - */ - static class SocketFactory extends JSSESocketFactory { - - private final SslStoreProvider sslStoreProvider; - - SocketFactory(AbstractEndpoint endpoint) { - super(endpoint); - this.sslStoreProvider = (SslStoreProvider) endpoint - .getAttribute("sslStoreProvider"); - } - - @Override - protected KeyStore getKeystore(String type, String provider, String pass) - throws IOException { - if (this.sslStoreProvider != null) { - try { - KeyStore store = this.sslStoreProvider.getKeyStore(); - if (store != null) { - return store; - } - } - catch (Exception ex) { - throw new IOException(ex); - } - } - return super.getKeystore(type, provider, pass); - } - - @Override - protected KeyStore getTrustStore(String keystoreType, String keystoreProvider) - throws IOException { - if (this.sslStoreProvider != null) { - try { - KeyStore store = this.sslStoreProvider.getTrustStore(); - if (store != null) { - return store; - } - } - catch (Exception ex) { - throw new IOException(ex); - } - } - return super.getTrustStore(keystoreType, keystoreProvider); - } - - } - -} diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index 96477db2e4..e3e40a7688 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -16,12 +16,19 @@ package org.springframework.boot.context.embedded.tomcat; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; import java.nio.charset.Charset; +import java.security.KeyStore; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -48,11 +55,13 @@ import org.apache.catalina.session.ManagerBase; import org.apache.catalina.session.StandardManager; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat.FixContextListener; +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.beans.BeanUtils; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; @@ -301,7 +310,7 @@ public class TomcatEmbeddedServletContainerFactory Compression compression = getCompression(); protocol.setCompression("on"); protocol.setCompressionMinSize(compression.getMinResponseSize()); - protocol.setCompressableMimeTypes( + protocol.setCompressableMimeType( StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes())); if (getCompression().getExcludedUserAgents() != null) { protocol.setNoCompressionUserAgents( @@ -323,13 +332,33 @@ public class TomcatEmbeddedServletContainerFactory protocol.setKeystorePass(ssl.getKeyStorePassword()); protocol.setKeyPass(ssl.getKeyPassword()); protocol.setKeyAlias(ssl.getKeyAlias()); - protocol.setCiphers(StringUtils.arrayToCommaDelimitedString(ssl.getCiphers())); + String ciphers = StringUtils.arrayToCommaDelimitedString(ssl.getCiphers()); + protocol.setCiphers(StringUtils.hasText(ciphers) ? ciphers : null); if (ssl.getEnabledProtocols() != null) { - protocol.setProperty("sslEnabledProtocols", - StringUtils.arrayToCommaDelimitedString(ssl.getEnabledProtocols())); + try { + for (SSLHostConfig sslHostConfig : protocol.findSslHostConfigs()) { + sslHostConfig.setProtocols(StringUtils + .arrayToCommaDelimitedString(ssl.getEnabledProtocols())); + } + } + catch (NoSuchMethodError ex) { + // Tomcat 8.0.x or earlier + Assert.isTrue( + protocol.setProperty("sslEnabledProtocols", + StringUtils.arrayToCommaDelimitedString( + ssl.getEnabledProtocols())), + "Failed to set sslEnabledProtocols"); + } } if (getSslStoreProvider() != null) { - configureSslStoreProvider(protocol, getSslStoreProvider()); + 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); @@ -350,10 +379,6 @@ public class TomcatEmbeddedServletContainerFactory SslStoreProvider sslStoreProvider) { Assert.isInstanceOf(Http11NioProtocol.class, protocol, "SslStoreProvider can only be used with Http11NioProtocol"); - ((Http11NioProtocol) protocol).getEndpoint().setAttribute("sslStoreProvider", - sslStoreProvider); - protocol.setSslImplementationName( - TomcatEmbeddedJSSEImplementation.class.getName()); } private void configureSslKeyStore(AbstractHttp11JsseProtocol protocol, Ssl ssl) { @@ -677,6 +702,89 @@ public class TomcatEmbeddedServletContainerFactory return this.uriEncoding; } + /** + * A {@link URLStreamHandlerFactory} that provides a {@link URLStreamHandler} for + * accessing an {@link SslStoreProvider}'s key store and trust store from a URL. + */ + private static final class SslStoreProviderUrlStreamHandlerFactory + implements URLStreamHandlerFactory { + + private static final String PROTOCOL = "springbootssl"; + + private static final String KEY_STORE_PATH = "keyStore"; + + private static final String KEY_STORE_URL = PROTOCOL + ":" + KEY_STORE_PATH; + + private static final String TRUST_STORE_PATH = "trustStore"; + + private static final String TRUST_STORE_URL = PROTOCOL + ":" + TRUST_STORE_PATH; + + private final SslStoreProvider sslStoreProvider; + + private SslStoreProviderUrlStreamHandlerFactory( + SslStoreProvider sslStoreProvider) { + this.sslStoreProvider = sslStoreProvider; + } + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + if (PROTOCOL.equals(protocol)) { + return new URLStreamHandler() { + + @Override + protected URLConnection openConnection(URL url) throws IOException { + try { + if (KEY_STORE_PATH.equals(url.getPath())) { + return new KeyStoreUrlConnection(url, + SslStoreProviderUrlStreamHandlerFactory.this.sslStoreProvider + .getKeyStore()); + } + if (TRUST_STORE_PATH.equals(url.getPath())) { + return new KeyStoreUrlConnection(url, + SslStoreProviderUrlStreamHandlerFactory.this.sslStoreProvider + .getTrustStore()); + } + } + catch (Exception ex) { + throw new IOException(ex); + } + throw new IOException("Invalid path: " + url.getPath()); + } + }; + } + return null; + } + + private static final class KeyStoreUrlConnection extends URLConnection { + + private final KeyStore keyStore; + + private KeyStoreUrlConnection(URL url, KeyStore keyStore) { + super(url); + this.keyStore = keyStore; + } + + @Override + public void connect() throws IOException { + + } + + @Override + public InputStream getInputStream() throws IOException { + + try { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + this.keyStore.store(stream, new char[0]); + return new ByteArrayInputStream(stream.toByteArray()); + } + catch (Exception ex) { + throw new IOException(ex); + } + } + + } + } + private static class TomcatErrorPage { private static final String ERROR_PAGE_CLASS = "org.apache.tomcat.util.descriptor.web.ErrorPage"; @@ -752,8 +860,7 @@ public class TomcatEmbeddedServletContainerFactory */ private static class StoreMergedWebXmlListener implements LifecycleListener { - @SuppressWarnings("deprecation") - private final String MERGED_WEB_XML = org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML; + private static final String MERGED_WEB_XML = "org.apache.tomcat.util.scan.MergedWebXml"; @Override public void lifecycleEvent(LifecycleEvent event) { @@ -764,8 +871,10 @@ public class TomcatEmbeddedServletContainerFactory private void onStart(Context context) { ServletContext servletContext = context.getServletContext(); - if (servletContext.getAttribute(this.MERGED_WEB_XML) == null) { - servletContext.setAttribute(this.MERGED_WEB_XML, getEmptyWebXml()); + if (servletContext + .getAttribute(StoreMergedWebXmlListener.MERGED_WEB_XML) == null) { + servletContext.setAttribute(StoreMergedWebXmlListener.MERGED_WEB_XML, + getEmptyWebXml()); } TomcatResources.get(context).addClasspathResources(); } diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index c80dc0c2a9..84608f01a8 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -40,7 +40,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; -import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -971,8 +971,8 @@ public class SpringApplicationTests { static class ExampleWebConfig { @Bean - public JettyEmbeddedServletContainerFactory container() { - return new JettyEmbeddedServletContainerFactory(0); + public TomcatEmbeddedServletContainerFactory container() { + return new TomcatEmbeddedServletContainerFactory(0); } } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java index faede699dd..f9cd1df718 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java @@ -55,6 +55,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.InputStreamFactory; import org.apache.http.client.protocol.HttpClientContext; @@ -124,6 +125,8 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { @BeforeClass @AfterClass public static void uninstallUrlStreamHandlerFactory() { + ReflectionTestUtils.setField(TomcatURLStreamHandlerFactory.class, "instance", + null); ReflectionTestUtils.setField(URL.class, "factory", null); } @@ -589,7 +592,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); } - private Ssl getSsl(ClientAuth clientAuth, String keyPassword, String keyStore) { + protected Ssl getSsl(ClientAuth clientAuth, String keyPassword, String keyStore) { return getSsl(clientAuth, keyPassword, keyStore, null, null, null); } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java index 2b57c02b09..8695ef7099 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java @@ -38,7 +38,7 @@ import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.valves.RemoteIpValve; -import org.apache.coyote.http11.AbstractHttp11JsseProtocol; +import org.apache.tomcat.util.net.SSLHostConfig; import org.junit.After; import org.junit.Rule; import org.junit.Test; @@ -266,16 +266,14 @@ public class TomcatEmbeddedServletContainerFactoryTests Tomcat tomcat = getTomcat(factory); Connector connector = tomcat.getConnector(); - AbstractHttp11JsseProtocol jsseProtocol = (AbstractHttp11JsseProtocol) connector - .getProtocolHandler(); - assertThat(jsseProtocol.getCiphers()).isEqualTo("ALPHA,BRAVO,CHARLIE"); + SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler() + .findSslHostConfigs(); + assertThat(sslHostConfigs[0].getCiphers()).isEqualTo("ALPHA:BRAVO:CHARLIE"); } @Test public void sslEnabledMultipleProtocolsConfiguration() throws Exception { - Ssl ssl = new Ssl(); - ssl.setKeyStore("test.jks"); - ssl.setKeyStorePassword("secret"); + 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" }); @@ -284,21 +282,20 @@ public class TomcatEmbeddedServletContainerFactoryTests this.container = factory .getEmbeddedServletContainer(sessionServletRegistration()); + this.container.start(); Tomcat tomcat = ((TomcatEmbeddedServletContainer) this.container).getTomcat(); Connector connector = tomcat.getConnector(); - AbstractHttp11JsseProtocol jsseProtocol = (AbstractHttp11JsseProtocol) connector - .getProtocolHandler(); - assertThat(jsseProtocol.getSslProtocol()).isEqualTo("TLS"); - assertThat(jsseProtocol.getProperty("sslEnabledProtocols")) - .isEqualTo("TLSv1.1,TLSv1.2"); + 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.setKeyStore("test.jks"); - ssl.setKeyStorePassword("secret"); + 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" }); @@ -310,10 +307,11 @@ public class TomcatEmbeddedServletContainerFactoryTests Tomcat tomcat = ((TomcatEmbeddedServletContainer) this.container).getTomcat(); Connector connector = tomcat.getConnector(); - AbstractHttp11JsseProtocol jsseProtocol = (AbstractHttp11JsseProtocol) connector - .getProtocolHandler(); - assertThat(jsseProtocol.getSslProtocol()).isEqualTo("TLS"); - assertThat(jsseProtocol.getProperty("sslEnabledProtocols")).isEqualTo("TLSv1.2"); + this.container.start(); + SSLHostConfig sslHostConfig = connector.getProtocolHandler() + .findSslHostConfigs()[0]; + assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS"); + assertThat(sslHostConfig.getEnabledProtocols()).containsExactly("TLSv1.2"); } @Test