Add support for configuring SSL declaratively
Both Tomcat and Jetty can now be configured to use SSL via the environment (typically application.properties or application.yml) Closes #1084
This commit is contained in:
parent
d26ecbef04
commit
0960908bd7
|
@ -33,6 +33,7 @@ import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletCont
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl;
|
||||||
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
|
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
|
||||||
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||||
|
@ -46,6 +47,7 @@ import org.springframework.util.StringUtils;
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = false)
|
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = false)
|
||||||
public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||||
|
@ -58,6 +60,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||||
|
|
||||||
private String contextPath;
|
private String contextPath;
|
||||||
|
|
||||||
|
private Ssl ssl;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String servletPath = "/";
|
private String servletPath = "/";
|
||||||
|
|
||||||
|
@ -131,6 +135,14 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||||
this.sessionTimeout = sessionTimeout;
|
this.sessionTimeout = sessionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Ssl getSsl() {
|
||||||
|
return this.ssl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSsl(Ssl ssl) {
|
||||||
|
this.ssl = ssl;
|
||||||
|
}
|
||||||
|
|
||||||
public void setLoader(String value) {
|
public void setLoader(String value) {
|
||||||
// no op to support Tomcat running as a traditional container (not embedded)
|
// no op to support Tomcat running as a traditional container (not embedded)
|
||||||
}
|
}
|
||||||
|
@ -149,12 +161,41 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||||
if (getSessionTimeout() != null) {
|
if (getSessionTimeout() != null) {
|
||||||
container.setSessionTimeout(getSessionTimeout());
|
container.setSessionTimeout(getSessionTimeout());
|
||||||
}
|
}
|
||||||
|
if (getSsl() != null) {
|
||||||
|
container.setSsl(getSsl());
|
||||||
|
}
|
||||||
if (container instanceof TomcatEmbeddedServletContainerFactory) {
|
if (container instanceof TomcatEmbeddedServletContainerFactory) {
|
||||||
getTomcat()
|
getTomcat()
|
||||||
.customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
|
.customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getPathsArray(Collection<String> paths) {
|
||||||
|
String[] result = new String[paths.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (String path : paths) {
|
||||||
|
result[i++] = getPath(path);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPathsArray(String[] paths) {
|
||||||
|
String[] result = new String[paths.length];
|
||||||
|
int i = 0;
|
||||||
|
for (String path : paths) {
|
||||||
|
result[i++] = getPath(path);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath(String path) {
|
||||||
|
String prefix = getServletPrefix();
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
return prefix + path;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Tomcat {
|
public static class Tomcat {
|
||||||
|
|
||||||
private String accessLogPattern;
|
private String accessLogPattern;
|
||||||
|
@ -313,31 +354,4 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getPathsArray(Collection<String> paths) {
|
|
||||||
String[] result = new String[paths.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (String path : paths) {
|
|
||||||
result[i++] = getPath(path);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getPathsArray(String[] paths) {
|
|
||||||
String[] result = new String[paths.length];
|
|
||||||
int i = 0;
|
|
||||||
for (String path : paths) {
|
|
||||||
result[i++] = getPath(path);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath(String path) {
|
|
||||||
String prefix = getServletPrefix();
|
|
||||||
if (!path.startsWith("/")) {
|
|
||||||
path = "/" + path;
|
|
||||||
}
|
|
||||||
return prefix + path;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,14 @@ content into your application; rather pick only the properties that you need.
|
||||||
server.session-timeout= # session timeout in seconds
|
server.session-timeout= # session timeout in seconds
|
||||||
server.context-path= # the context path, defaults to '/'
|
server.context-path= # the context path, defaults to '/'
|
||||||
server.servlet-path= # the servlet path, defaults to '/'
|
server.servlet-path= # the servlet path, defaults to '/'
|
||||||
|
server.ssl.client-auth= # want or need
|
||||||
|
server.ssl.key-alias=
|
||||||
|
server.ssl.key-password=
|
||||||
|
server.ssl.key-store=
|
||||||
|
server.ssl.key-store-password=
|
||||||
|
server.ssl.protocol=TLS
|
||||||
|
server.ssl.trust-store=
|
||||||
|
server.ssl.trust-store-password=
|
||||||
server.tomcat.access-log-pattern= # log pattern of the access log
|
server.tomcat.access-log-pattern= # log pattern of the access log
|
||||||
server.tomcat.access-log-enabled=false # is access logging enabled
|
server.tomcat.access-log-enabled=false # is access logging enabled
|
||||||
server.tomcat.protocol-header=x-forwarded-proto # ssl forward headers
|
server.tomcat.protocol-header=x-forwarded-proto # ssl forward headers
|
||||||
|
|
|
@ -387,6 +387,25 @@ and then inject the actual (``local'') port as a `@Value`. For example:
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[howto-configure-ssl]]
|
||||||
|
=== Configure SSL
|
||||||
|
SSL can be configured declaratively by setting the various `server.ssl.*` properties,
|
||||||
|
typically in `application.properties` or `application.yml`. For example:
|
||||||
|
|
||||||
|
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
|
||||||
|
----
|
||||||
|
server.port = 8443
|
||||||
|
server.ssl.key-store = classpath:keystore.jks
|
||||||
|
server.ssl.key-store-password = secret
|
||||||
|
server.ssl.key-password = another-secret
|
||||||
|
----
|
||||||
|
|
||||||
|
See {sc-spring-boot}/context/embedded/Ssl.{sc-ext}[`Ssl`] for details of all of the
|
||||||
|
supported properties.
|
||||||
|
|
||||||
|
NOTE: Tomcat requires the key store (and trust store if you're using one) to be directly
|
||||||
|
accessible on the filesystem, i.e. it cannot be read from within a jar file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[howto-configure-tomcat]]
|
[[howto-configure-tomcat]]
|
||||||
|
@ -401,56 +420,6 @@ nuclear option is to add your own `TomcatEmbeddedServletContainerFactory`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[howto-terminate-ssl-in-tomcat]]
|
|
||||||
=== Terminate SSL in Tomcat
|
|
||||||
Use an `EmbeddedServletContainerCustomizer` and in that add a `TomcatConnectorCustomizer`
|
|
||||||
that sets up the connector to be secure:
|
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes,attributes"]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public EmbeddedServletContainerCustomizer containerCustomizer(){
|
|
||||||
return new MyCustomizer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customize(ConfigurableEmbeddedServletContainer factory) {
|
|
||||||
if(factory instanceof TomcatEmbeddedServletContainerFactory) {
|
|
||||||
customizeTomcat((TomcatEmbeddedServletContainerFactory) factory));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void customizeTomcat(TomcatEmbeddedServletContainerFactory factory) {
|
|
||||||
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
|
|
||||||
@Override
|
|
||||||
public void customize(Connector connector) {
|
|
||||||
connector.setPort(serverPort);
|
|
||||||
connector.setSecure(true);
|
|
||||||
connector.setScheme("https");
|
|
||||||
connector.setAttribute("keyAlias", "tomcat");
|
|
||||||
connector.setAttribute("keystorePass", "password");
|
|
||||||
try {
|
|
||||||
connector.setAttribute("keystoreFile",
|
|
||||||
ResourceUtils.getFile("src/ssl/tomcat.keystore").getAbsolutePath());
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new IllegalStateException("Cannot load keystore", e);
|
|
||||||
}
|
|
||||||
connector.setAttribute("clientAuth", "false");
|
|
||||||
connector.setAttribute("sslProtocol", "TLS");
|
|
||||||
connector.setAttribute("SSLEnabled", true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[howto-enable-multiple-connectors-in-tomcat]]
|
[[howto-enable-multiple-connectors-in-tomcat]]
|
||||||
=== Enable Multiple Connectors Tomcat
|
=== Enable Multiple Connectors Tomcat
|
||||||
Add a `org.apache.catalina.connector.Connector` to the
|
Add a `org.apache.catalina.connector.Connector` to the
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
<module>spring-boot-sample-secure</module>
|
<module>spring-boot-sample-secure</module>
|
||||||
<module>spring-boot-sample-servlet</module>
|
<module>spring-boot-sample-servlet</module>
|
||||||
<module>spring-boot-sample-simple</module>
|
<module>spring-boot-sample-simple</module>
|
||||||
|
<module>spring-boot-sample-tomcat-ssl</module>
|
||||||
<module>spring-boot-sample-tomcat</module>
|
<module>spring-boot-sample-tomcat</module>
|
||||||
<module>spring-boot-sample-tomcat-multi-connectors</module>
|
<module>spring-boot-sample-tomcat-multi-connectors</module>
|
||||||
<module>spring-boot-sample-tomcat8-jsp</module>
|
<module>spring-boot-sample-tomcat8-jsp</module>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<!-- Your own application should inherit from spring-boot-starter-parent -->
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-samples</artifactId>
|
||||||
|
<version>1.2.0.BUILD-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>spring-boot-sample-tomcat-ssl</artifactId>
|
||||||
|
<name>Spring Boot Tomcat Sample</name>
|
||||||
|
<description>Spring Boot Tomcat SSL Sample</description>
|
||||||
|
<url>http://projects.spring.io/spring-boot/</url>
|
||||||
|
<organization>
|
||||||
|
<name>Pivotal Software, Inc.</name>
|
||||||
|
<url>http://www.spring.io</url>
|
||||||
|
</organization>
|
||||||
|
<properties>
|
||||||
|
<main.basedir>${basedir}/../..</main.basedir>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
Binary file not shown.
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@ComponentScan
|
||||||
|
@Configuration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@EnableConfigurationProperties
|
||||||
|
public class SampleTomcatSslApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SpringApplication.run(SampleTomcatSslApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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.web;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class SampleController {
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
@ResponseBody
|
||||||
|
public String helloWorld() {
|
||||||
|
return "Hello, world";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
server.port = 8443
|
||||||
|
server.ssl.key-store = sample.jks
|
||||||
|
server.ssl.key-store-password = secret
|
||||||
|
server.ssl.key-password = password
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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;
|
||||||
|
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||||
|
import org.apache.http.conn.ssl.SSLContextBuilder;
|
||||||
|
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.test.IntegrationTest;
|
||||||
|
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||||
|
import org.springframework.boot.test.TestRestTemplate;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringApplicationConfiguration(classes = SampleTomcatSslApplication.class)
|
||||||
|
@WebAppConfiguration
|
||||||
|
@IntegrationTest("server.port:0")
|
||||||
|
@DirtiesContext
|
||||||
|
public class SampleTomcatSslApplicationTests {
|
||||||
|
|
||||||
|
@Value("${local.server.port}")
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHome() throws Exception {
|
||||||
|
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||||
|
new SSLContextBuilder().loadTrustMaterial(null,
|
||||||
|
new TrustSelfSignedStrategy()).build());
|
||||||
|
|
||||||
|
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TestRestTemplate testRestTemplate = new TestRestTemplate();
|
||||||
|
((HttpComponentsClientHttpRequestFactory) testRestTemplate.getRequestFactory())
|
||||||
|
.setHttpClient(httpClient);
|
||||||
|
ResponseEntity<String> entity = testRestTemplate.getForEntity(
|
||||||
|
"https://localhost:" + this.port, String.class);
|
||||||
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
|
assertEquals("Hello, world", entity.getBody());
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,6 +59,8 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
|
||||||
|
|
||||||
private int sessionTimeout;
|
private int sessionTimeout;
|
||||||
|
|
||||||
|
private Ssl ssl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link AbstractConfigurableEmbeddedServletContainer} instance.
|
* Create a new {@link AbstractConfigurableEmbeddedServletContainer} instance.
|
||||||
*/
|
*/
|
||||||
|
@ -247,6 +249,15 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
|
||||||
this.jspServletClassName = jspServletClassName;
|
this.jspServletClassName = jspServletClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSsl(Ssl ssl) {
|
||||||
|
this.ssl = ssl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ssl getSsl() {
|
||||||
|
return this.ssl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the JSP servlet class name
|
* @return the JSP servlet class name
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -139,4 +139,10 @@ public interface ConfigurableEmbeddedServletContainer {
|
||||||
*/
|
*/
|
||||||
void addInitializers(ServletContextInitializer... initializers);
|
void addInitializers(ServletContextInitializer... initializers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the SSL configuration that will be applied to the container's default
|
||||||
|
* connector.
|
||||||
|
* @param ssl the SSL configuration
|
||||||
|
*/
|
||||||
|
void setSsl(Ssl ssl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple container-independent abstraction for SSL configuration.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class Ssl {
|
||||||
|
|
||||||
|
private ClientAuth clientAuth;
|
||||||
|
|
||||||
|
private String[] ciphers;
|
||||||
|
|
||||||
|
private String keyAlias;
|
||||||
|
|
||||||
|
private String keyPassword;
|
||||||
|
|
||||||
|
private String keyStore;
|
||||||
|
|
||||||
|
private String keyStorePassword;
|
||||||
|
|
||||||
|
private String trustStore;
|
||||||
|
|
||||||
|
private String trustStorePassword;
|
||||||
|
|
||||||
|
private String protocol = "TLS";
|
||||||
|
|
||||||
|
public ClientAuth getClientAuth() {
|
||||||
|
return this.clientAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientAuth(ClientAuth clientAuth) {
|
||||||
|
this.clientAuth = clientAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getCiphers() {
|
||||||
|
return this.ciphers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCiphers(String[] ciphers) {
|
||||||
|
this.ciphers = ciphers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyAlias() {
|
||||||
|
return this.keyAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyAlias(String keyAlias) {
|
||||||
|
this.keyAlias = keyAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyPassword() {
|
||||||
|
return this.keyPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyPassword(String keyPassword) {
|
||||||
|
this.keyPassword = keyPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyStore() {
|
||||||
|
return this.keyStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyStore(String keyStore) {
|
||||||
|
this.keyStore = keyStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyStorePassword() {
|
||||||
|
return this.keyStorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyStorePassword(String keyStorePassword) {
|
||||||
|
this.keyStorePassword = keyStorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTrustStore() {
|
||||||
|
return this.trustStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrustStore(String trustStore) {
|
||||||
|
this.trustStore = trustStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTrustStorePassword() {
|
||||||
|
return this.trustStorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrustStorePassword(String trustStorePassword) {
|
||||||
|
this.trustStorePassword = trustStorePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtocol() {
|
||||||
|
return this.protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtocol(String protocol) {
|
||||||
|
this.protocol = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ClientAuth {
|
||||||
|
WANT, NEED;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.context.embedded.jetty;
|
package org.springframework.boot.context.embedded.jetty;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -24,25 +25,32 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||||
|
import org.eclipse.jetty.server.ssl.SslSocketConnector;
|
||||||
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.eclipse.jetty.servlet.ServletMapping;
|
import org.eclipse.jetty.servlet.ServletMapping;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
||||||
import org.eclipse.jetty.webapp.Configuration;
|
import org.eclipse.jetty.webapp.Configuration;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||||
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
||||||
import org.springframework.boot.context.embedded.ErrorPage;
|
import org.springframework.boot.context.embedded.ErrorPage;
|
||||||
import org.springframework.boot.context.embedded.MimeMappings;
|
import org.springframework.boot.context.embedded.MimeMappings;
|
||||||
import org.springframework.boot.context.embedded.ServletContextInitializer;
|
import org.springframework.boot.context.embedded.ServletContextInitializer;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
|
||||||
import org.springframework.context.ResourceLoaderAware;
|
import org.springframework.context.ResourceLoaderAware;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.ResourceUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,6 +112,16 @@ public class JettyEmbeddedServletContainerFactory extends
|
||||||
configureWebAppContext(context, initializers);
|
configureWebAppContext(context, initializers);
|
||||||
server.setHandler(context);
|
server.setHandler(context);
|
||||||
this.logger.info("Server initialized with port: " + port);
|
this.logger.info("Server initialized with port: " + port);
|
||||||
|
|
||||||
|
if (getSsl() != null) {
|
||||||
|
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||||
|
configureSslContextFactory(sslContextFactory, getSsl());
|
||||||
|
|
||||||
|
SslSocketConnector sslConnector = new SslSocketConnector(sslContextFactory);
|
||||||
|
sslConnector.setPort(port);
|
||||||
|
server.setConnectors(new Connector[] { sslConnector });
|
||||||
|
}
|
||||||
|
|
||||||
for (JettyServerCustomizer customizer : getServerCustomizers()) {
|
for (JettyServerCustomizer customizer : getServerCustomizers()) {
|
||||||
customizer.customize(server);
|
customizer.customize(server);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +129,52 @@ public class JettyEmbeddedServletContainerFactory extends
|
||||||
return getJettyEmbeddedServletContainer(server);
|
return getJettyEmbeddedServletContainer(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void configureSslContextFactory(SslContextFactory sslContextFactory, Ssl ssl) {
|
||||||
|
sslContextFactory.setProtocol(getSsl().getProtocol());
|
||||||
|
if (getSsl().getClientAuth() == ClientAuth.NEED) {
|
||||||
|
sslContextFactory.setNeedClientAuth(true);
|
||||||
|
sslContextFactory.setWantClientAuth(true);
|
||||||
|
}
|
||||||
|
else if (getSsl().getClientAuth() == ClientAuth.WANT) {
|
||||||
|
sslContextFactory.setWantClientAuth(true);
|
||||||
|
}
|
||||||
|
if (getSsl().getKeyStorePassword() != null) {
|
||||||
|
sslContextFactory.setKeyStorePassword(getSsl().getKeyStorePassword());
|
||||||
|
}
|
||||||
|
if (getSsl().getKeyPassword() != null) {
|
||||||
|
sslContextFactory.setKeyManagerPassword(getSsl().getKeyPassword());
|
||||||
|
}
|
||||||
|
sslContextFactory.setCertAlias(getSsl().getKeyAlias());
|
||||||
|
try {
|
||||||
|
sslContextFactory.setKeyStoreResource(Resource.newResource(ResourceUtils
|
||||||
|
.getURL(getSsl().getKeyStore())));
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new EmbeddedServletContainerException("Could not find key store '"
|
||||||
|
+ getSsl().getKeyStore() + "'", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSsl().getCiphers() != null) {
|
||||||
|
sslContextFactory.setIncludeCipherSuites(getSsl().getCiphers());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSsl().getTrustStorePassword() != null) {
|
||||||
|
sslContextFactory.setTrustStorePassword(getSsl().getTrustStorePassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getSsl().getTrustStore() != null) {
|
||||||
|
try {
|
||||||
|
sslContextFactory.setTrustStoreResource(Resource
|
||||||
|
.newResource(ResourceUtils.getURL(getSsl().getTrustStore())));
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new EmbeddedServletContainerException(
|
||||||
|
"Could not find trust store '" + getSsl().getTrustStore() + "'",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the given Jetty {@link WebAppContext} for use.
|
* Configure the given Jetty {@link WebAppContext} for use.
|
||||||
* @param context the context to configure
|
* @param context the context to configure
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.context.embedded.tomcat;
|
package org.springframework.boot.context.embedded.tomcat;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -41,6 +42,7 @@ import org.apache.catalina.loader.WebappLoader;
|
||||||
import org.apache.catalina.startup.Tomcat;
|
import org.apache.catalina.startup.Tomcat;
|
||||||
import org.apache.catalina.startup.Tomcat.FixContextListener;
|
import org.apache.catalina.startup.Tomcat.FixContextListener;
|
||||||
import org.apache.coyote.AbstractProtocol;
|
import org.apache.coyote.AbstractProtocol;
|
||||||
|
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||||
|
@ -49,12 +51,16 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory
|
||||||
import org.springframework.boot.context.embedded.ErrorPage;
|
import org.springframework.boot.context.embedded.ErrorPage;
|
||||||
import org.springframework.boot.context.embedded.MimeMappings;
|
import org.springframework.boot.context.embedded.MimeMappings;
|
||||||
import org.springframework.boot.context.embedded.ServletContextInitializer;
|
import org.springframework.boot.context.embedded.ServletContextInitializer;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
|
||||||
import org.springframework.context.ResourceLoaderAware;
|
import org.springframework.context.ResourceLoaderAware;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.ResourceUtils;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link EmbeddedServletContainerFactory} that can be used to create
|
* {@link EmbeddedServletContainerFactory} that can be used to create
|
||||||
|
@ -230,11 +236,64 @@ public class TomcatEmbeddedServletContainerFactory extends
|
||||||
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
|
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
|
||||||
// prematurely...
|
// prematurely...
|
||||||
connector.setProperty("bindOnInit", "false");
|
connector.setProperty("bindOnInit", "false");
|
||||||
|
|
||||||
|
if (getSsl() != null) {
|
||||||
|
if (connector.getProtocolHandler() instanceof AbstractHttp11JsseProtocol) {
|
||||||
|
AbstractHttp11JsseProtocol jsseProtocol = (AbstractHttp11JsseProtocol) connector
|
||||||
|
.getProtocolHandler();
|
||||||
|
configureJsseProtocol(jsseProtocol, getSsl());
|
||||||
|
connector.setScheme("https");
|
||||||
|
connector.setSecure(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"To use SSL, the connector's protocol handler must be an AbstractHttp11JsseProtocol subclass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
|
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
|
||||||
customizer.customize(connector);
|
customizer.customize(connector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void configureJsseProtocol(AbstractHttp11JsseProtocol jsseProtocol, Ssl ssl) {
|
||||||
|
jsseProtocol.setSSLEnabled(true);
|
||||||
|
jsseProtocol.setSslProtocol(getSsl().getProtocol());
|
||||||
|
if (getSsl().getClientAuth() == ClientAuth.NEED) {
|
||||||
|
jsseProtocol.setClientAuth(Boolean.TRUE.toString());
|
||||||
|
}
|
||||||
|
else if (getSsl().getClientAuth() == ClientAuth.WANT) {
|
||||||
|
jsseProtocol.setClientAuth("want");
|
||||||
|
}
|
||||||
|
jsseProtocol.setKeystorePass(getSsl().getKeyStorePassword());
|
||||||
|
jsseProtocol.setKeyPass(getSsl().getKeyPassword());
|
||||||
|
jsseProtocol.setKeyAlias(getSsl().getKeyAlias());
|
||||||
|
try {
|
||||||
|
jsseProtocol.setKeystoreFile(ResourceUtils.getFile(getSsl().getKeyStore())
|
||||||
|
.getAbsolutePath());
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
throw new EmbeddedServletContainerException("Could not find key store "
|
||||||
|
+ getSsl().getKeyStore(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
jsseProtocol.setCiphers(StringUtils.arrayToCommaDelimitedString(getSsl()
|
||||||
|
.getCiphers()));
|
||||||
|
|
||||||
|
if (getSsl().getTrustStore() != null) {
|
||||||
|
try {
|
||||||
|
jsseProtocol.setTruststoreFile(ResourceUtils.getFile(
|
||||||
|
getSsl().getTrustStore()).getAbsolutePath());
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
throw new EmbeddedServletContainerException("Could not find trust store "
|
||||||
|
+ getSsl().getTrustStore(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsseProtocol.setTruststorePass(getSsl().getTrustStorePassword());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the Tomcat {@link Context}.
|
* Configure the Tomcat {@link Context}.
|
||||||
* @param context the Tomcat context
|
* @param context the Tomcat context
|
||||||
|
|
|
@ -16,11 +16,14 @@
|
||||||
|
|
||||||
package org.springframework.boot.context.embedded;
|
package org.springframework.boot.context.embedded;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.KeyStore;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -31,12 +34,18 @@ import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||||
|
import org.apache.http.conn.ssl.SSLContextBuilder;
|
||||||
|
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.client.ClientHttpRequest;
|
import org.springframework.http.client.ClientHttpRequest;
|
||||||
|
@ -62,6 +71,7 @@ import static org.mockito.Mockito.mock;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Greg Turnquist
|
* @author Greg Turnquist
|
||||||
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
||||||
|
|
||||||
|
@ -300,8 +310,192 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
||||||
assertThat(getResponse(getLocalUrl("/bang")), equalTo("Hello World"));
|
assertThat(getResponse(getLocalUrl("/bang")), equalTo("Hello World"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicSsl() throws Exception {
|
||||||
|
FileCopyUtils.copy("test",
|
||||||
|
new FileWriter(this.temporaryFolder.newFile("test.txt")));
|
||||||
|
|
||||||
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setDocumentRoot(this.temporaryFolder.getRoot());
|
||||||
|
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("src/test/resources/test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setKeyPassword("password");
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
|
this.container.start();
|
||||||
|
|
||||||
|
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||||
|
new SSLContextBuilder().loadTrustMaterial(null,
|
||||||
|
new TrustSelfSignedStrategy()).build());
|
||||||
|
|
||||||
|
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
|
||||||
|
httpClient);
|
||||||
|
|
||||||
|
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory),
|
||||||
|
equalTo("test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sslNeedsClientAuthenticationSucceedsWithClientCertificate()
|
||||||
|
throws Exception {
|
||||||
|
FileCopyUtils.copy("test",
|
||||||
|
new FileWriter(this.temporaryFolder.newFile("test.txt")));
|
||||||
|
|
||||||
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setDocumentRoot(this.temporaryFolder.getRoot());
|
||||||
|
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("src/test/resources/test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setKeyPassword("password");
|
||||||
|
ssl.setClientAuth(ClientAuth.NEED);
|
||||||
|
ssl.setTrustStore("src/test/resources/test.jks");
|
||||||
|
ssl.setTrustStorePassword("secret");
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
|
this.container.start();
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
keyStore.load(new FileInputStream(new File("src/test/resources/test.jks")),
|
||||||
|
"secret".toCharArray());
|
||||||
|
|
||||||
|
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||||
|
new SSLContextBuilder()
|
||||||
|
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
|
||||||
|
.loadKeyMaterial(keyStore, "password".toCharArray()).build());
|
||||||
|
|
||||||
|
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
|
||||||
|
httpClient);
|
||||||
|
|
||||||
|
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory),
|
||||||
|
equalTo("test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void sslNeedsClientAuthenticationFailsWithoutClientCertificate()
|
||||||
|
throws Exception {
|
||||||
|
FileCopyUtils.copy("test",
|
||||||
|
new FileWriter(this.temporaryFolder.newFile("test.txt")));
|
||||||
|
|
||||||
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setDocumentRoot(this.temporaryFolder.getRoot());
|
||||||
|
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("src/test/resources/test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setKeyPassword("password");
|
||||||
|
ssl.setClientAuth(ClientAuth.NEED);
|
||||||
|
ssl.setTrustStore("src/test/resources/test.jks");
|
||||||
|
ssl.setTrustStorePassword("secret");
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
|
this.container.start();
|
||||||
|
|
||||||
|
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||||
|
new SSLContextBuilder().loadTrustMaterial(null,
|
||||||
|
new TrustSelfSignedStrategy()).build());
|
||||||
|
|
||||||
|
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
|
||||||
|
httpClient);
|
||||||
|
|
||||||
|
getResponse(getLocalUrl("https", "/test.txt"), requestFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sslWantsClientAuthenticationSucceedsWithClientCertificate()
|
||||||
|
throws Exception {
|
||||||
|
FileCopyUtils.copy("test",
|
||||||
|
new FileWriter(this.temporaryFolder.newFile("test.txt")));
|
||||||
|
|
||||||
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setDocumentRoot(this.temporaryFolder.getRoot());
|
||||||
|
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("src/test/resources/test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setKeyPassword("password");
|
||||||
|
ssl.setClientAuth(ClientAuth.WANT);
|
||||||
|
ssl.setTrustStore("src/test/resources/test.jks");
|
||||||
|
ssl.setTrustStorePassword("secret");
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
|
this.container.start();
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
keyStore.load(new FileInputStream(new File("src/test/resources/test.jks")),
|
||||||
|
"secret".toCharArray());
|
||||||
|
|
||||||
|
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||||
|
new SSLContextBuilder()
|
||||||
|
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
|
||||||
|
.loadKeyMaterial(keyStore, "password".toCharArray()).build());
|
||||||
|
|
||||||
|
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
|
||||||
|
httpClient);
|
||||||
|
|
||||||
|
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory),
|
||||||
|
equalTo("test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sslWantsClientAuthenticationSucceedsWithoutClientCertificate()
|
||||||
|
throws Exception {
|
||||||
|
FileCopyUtils.copy("test",
|
||||||
|
new FileWriter(this.temporaryFolder.newFile("test.txt")));
|
||||||
|
|
||||||
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setDocumentRoot(this.temporaryFolder.getRoot());
|
||||||
|
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("src/test/resources/test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setKeyPassword("password");
|
||||||
|
ssl.setClientAuth(ClientAuth.WANT);
|
||||||
|
ssl.setTrustStore("src/test/resources/test.jks");
|
||||||
|
ssl.setTrustStorePassword("secret");
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
|
this.container.start();
|
||||||
|
|
||||||
|
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||||
|
new SSLContextBuilder().loadTrustMaterial(null,
|
||||||
|
new TrustSelfSignedStrategy()).build());
|
||||||
|
|
||||||
|
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
|
||||||
|
httpClient);
|
||||||
|
|
||||||
|
assertThat(getResponse(getLocalUrl("https", "/test.txt"), requestFactory),
|
||||||
|
equalTo("test"));
|
||||||
|
}
|
||||||
|
|
||||||
protected String getLocalUrl(String resourcePath) {
|
protected String getLocalUrl(String resourcePath) {
|
||||||
return "http://localhost:" + this.container.getPort() + resourcePath;
|
return getLocalUrl("http", resourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getLocalUrl(String scheme, String resourcePath) {
|
||||||
|
return scheme + "://localhost:" + this.container.getPort() + resourcePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getLocalUrl(int port, String resourcePath) {
|
protected String getLocalUrl(int port, String resourcePath) {
|
||||||
|
@ -318,10 +512,27 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getResponse(String url,
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory) throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
ClientHttpResponse response = getClientResponse(url, requestFactory);
|
||||||
|
try {
|
||||||
|
return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected ClientHttpResponse getClientResponse(String url) throws IOException,
|
protected ClientHttpResponse getClientResponse(String url) throws IOException,
|
||||||
URISyntaxException {
|
URISyntaxException {
|
||||||
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
|
return getClientResponse(url, new HttpComponentsClientHttpRequestFactory());
|
||||||
ClientHttpRequest request = clientHttpRequestFactory.createRequest(new URI(url),
|
}
|
||||||
|
|
||||||
|
protected ClientHttpResponse getClientResponse(String url,
|
||||||
|
HttpComponentsClientHttpRequestFactory requestFactory) throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
ClientHttpRequest request = requestFactory.createRequest(new URI(url),
|
||||||
HttpMethod.GET);
|
HttpMethod.GET);
|
||||||
ClientHttpResponse response = request.execute();
|
ClientHttpResponse response = request.execute();
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -21,11 +21,13 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ssl.SslConnector;
|
||||||
import org.eclipse.jetty.webapp.Configuration;
|
import org.eclipse.jetty.webapp.Configuration;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
|
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
@ -39,6 +41,7 @@ import static org.mockito.Mockito.mock;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
public class JettyEmbeddedServletContainerFactoryTests extends
|
public class JettyEmbeddedServletContainerFactoryTests extends
|
||||||
AbstractEmbeddedServletContainerFactoryTests {
|
AbstractEmbeddedServletContainerFactoryTests {
|
||||||
|
@ -94,6 +97,25 @@ public class JettyEmbeddedServletContainerFactoryTests extends
|
||||||
assertTimeout(factory, 60);
|
assertTimeout(factory, 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sslCiphersConfiguration() throws Exception {
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("src/test/resources/test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setKeyPassword("password");
|
||||||
|
ssl.setCiphers(new String[] { "ALPHA", "BRAVO", "CHARLIE" });
|
||||||
|
|
||||||
|
JettyEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
|
JettyEmbeddedServletContainer jettyContainer = (JettyEmbeddedServletContainer) this.container;
|
||||||
|
SslConnector sslConnector = (SslConnector) jettyContainer.getServer()
|
||||||
|
.getConnectors()[0];
|
||||||
|
assertThat(sslConnector.getSslContextFactory().getIncludeCipherSuites(),
|
||||||
|
equalTo(new String[] { "ALPHA", "BRAVO", "CHARLIE" }));
|
||||||
|
}
|
||||||
|
|
||||||
private void assertTimeout(JettyEmbeddedServletContainerFactory factory, int expected) {
|
private void assertTimeout(JettyEmbeddedServletContainerFactory factory, int expected) {
|
||||||
this.container = factory.getEmbeddedServletContainer();
|
this.container = factory.getEmbeddedServletContainer();
|
||||||
JettyEmbeddedServletContainer jettyContainer = (JettyEmbeddedServletContainer) this.container;
|
JettyEmbeddedServletContainer jettyContainer = (JettyEmbeddedServletContainer) this.container;
|
||||||
|
|
|
@ -28,9 +28,11 @@ import org.apache.catalina.Service;
|
||||||
import org.apache.catalina.Valve;
|
import org.apache.catalina.Valve;
|
||||||
import org.apache.catalina.connector.Connector;
|
import org.apache.catalina.connector.Connector;
|
||||||
import org.apache.catalina.startup.Tomcat;
|
import org.apache.catalina.startup.Tomcat;
|
||||||
|
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
|
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
|
||||||
|
import org.springframework.boot.context.embedded.Ssl;
|
||||||
import org.springframework.util.SocketUtils;
|
import org.springframework.util.SocketUtils;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
@ -221,6 +223,24 @@ public class TomcatEmbeddedServletContainerFactoryTests extends
|
||||||
assertEquals("UTF-8", tomcat.getConnector().getURIEncoding());
|
assertEquals("UTF-8", tomcat.getConnector().getURIEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sslCiphersConfiguration() throws Exception {
|
||||||
|
Ssl ssl = new Ssl();
|
||||||
|
ssl.setKeyStore("test.jks");
|
||||||
|
ssl.setKeyStorePassword("secret");
|
||||||
|
ssl.setCiphers(new String[] { "ALPHA", "BRAVO", "CHARLIE" });
|
||||||
|
|
||||||
|
TomcatEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
factory.setSsl(ssl);
|
||||||
|
|
||||||
|
Tomcat tomcat = getTomcat(factory);
|
||||||
|
Connector connector = tomcat.getConnector();
|
||||||
|
|
||||||
|
AbstractHttp11JsseProtocol jsseProtocol = (AbstractHttp11JsseProtocol) connector
|
||||||
|
.getProtocolHandler();
|
||||||
|
assertThat(jsseProtocol.getCiphers(), equalTo("ALPHA,BRAVO,CHARLIE"));
|
||||||
|
}
|
||||||
|
|
||||||
private void assertTimeout(TomcatEmbeddedServletContainerFactory factory, int expected) {
|
private void assertTimeout(TomcatEmbeddedServletContainerFactory factory, int expected) {
|
||||||
Tomcat tomcat = getTomcat(factory);
|
Tomcat tomcat = getTomcat(factory);
|
||||||
Context context = (Context) tomcat.getHost().findChildren()[0];
|
Context context = (Context) tomcat.getHost().findChildren()[0];
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue