55

I'm trying to set up Spring Security to work with Spring Boot's embedded Tomcat instance. There are quite a few basic samples that do this but I'm stuck where they leave off -- they do basic authentication over HTTP (not HTTPS).

I could probably make it work if I had access to the Tomcat configuration files (server.xml) but since Spring Boot uses an embedded Tomcat instance (which is otherwise a huge convenience), I dont have access to the Tomcat configuration files (at least, not to my knowledge).

There may be an application.properties setting for this but I haven't been able to track it down. I've seen references to a server.contextPath field in application.properties that I suspect may have something to do with replacement Tomcat config files. Even if it is related, I wouldn't know where to begin anyway -- all of the Tomcat SSL instructions I've seen start with editing an existing server.xml file, not building one from scratch.

Can this be done with Spring Boot (either by somehow specifying a snippet of server.xml or through other means)? If not, what would be the simplest way to do this? I understand that I may need to exclude the Tomcat component of Spring Boot but I'd prefer to avoid that if possible.

nbro
  • 15,395
  • 32
  • 113
  • 196
Dave
  • 7,589
  • 12
  • 36
  • 42
  • 1
    I've found another example that uses a different `application.properties` setting, `server.tomcat.basedir`, that strikes me as being more likely to be related to embedded Tomcat configuration. I can't get it to do anything either but its probably closer to the right direction and may make my question clearer. – Dave Oct 27 '13 at 03:46
  • 1
    No luck with the command-line environment variables: `-Djavax.net.ssl.keyStore=/path/to/keystore` `-Djavax.net.ssl.keyStorePassword=keyStorePass` either. – Dave Oct 27 '13 at 10:14

5 Answers5

75

Starting with Spring Boot 1.2, you can configure SSL using application.properties or application.yml. Here's an example for application.properties:

server.port = 8443
server.ssl.key-store = classpath:keystore.jks
server.ssl.key-store-password = secret
server.ssl.key-password = another-secret

Same thing with application.yml:

server:
  port: 8443
  ssl:
    key-store: classpath:keystore.jks
    key-store-password: secret
    key-password: another-secret

Here's a link to the current reference documentation.

  • 4
    what is the difference between giving these SSL properties via application.yml and having our own implementation of TomcatConnectorCustomizer (as in below answer) ? – yathirigan Sep 09 '15 at 09:56
  • 8
    Simplicity vs control. –  Sep 09 '15 at 12:27
  • can we have two keystores in the project and how to add within same property file? is that possible? – Vignesh_A Mar 04 '20 at 07:16
25

For external keystores, prefix with "file:"

server.ssl.key-store=file:config/keystore 
pma
  • 860
  • 1
  • 9
  • 26
  • 2
    This might have worked in the past. Now you don't put the "file:". Just put "server.ssl.key-store=config/keystore.jks". – Craigo Nov 12 '21 at 02:56
21

It turns out that there is a way to do this, although I'm not sure I've found the 'proper' way since this required hours of reading source code from multiple projects. In other words, this might be a lot of dumb work (but it works).

First, there is no way to get at the server.xml in the embedded Tomcat, either to augment it or replace it. This must be done programmatically.

Second, the 'require_https' setting doesn't help since you can't set cert info that way. It does set up forwarding from http to https, but it doesn't give you a way to make https work so the forwarding isnt helpful. However, use it with the stuff below, which does make https work.

To begin, you need to provide an EmbeddedServletContainerFactory as explained in the Embedded Servlet Container Support docs. The docs are for Java but the Groovy would look pretty much the same. Note that I haven't been able to get it to recognize the @Value annotation used in their example but its not needed. For groovy, simply put this in a new .groovy file and include that file on the command line when you launch spring boot.

Now, the instructions say that you can customize the TomcatEmbeddedServletContainerFactory class that you created in that code so that you can alter web.xml behavior, and this is true, but for our purposes its important to know that you can also use it to tailor server.xml behavior. Indeed, reading the source for the class and comparing it with the Embedded Tomcat docs, you see that this is the only place to do that. The interesting function is TomcatEmbeddedServletContainerFactory.addConnectorCustomizers(), which may not look like much from the Javadocs but actually gives you the Embedded Tomcat object to customize yourself. Simply pass your own implementation of TomcatConnectorCustomizer and set the things you want on the given Connector in the void customize(Connector con) function. Now, there are about a billion things you can do with the Connector and I couldn't find useful docs for it but the createConnector() function in this this guys personal Spring-embedded-Tomcat project is a very practical guide. My implementation ended up looking like this:

package com.deepdownstudios.server

import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.boot.*
import org.springframework.stereotype.*

@Configuration
class MyConfiguration {

@Bean
public EmbeddedServletContainerFactory servletContainer() {
final int port = 8443;
final String keystoreFile = "/path/to/keystore"
final String keystorePass = "keystore-password"
final String keystoreType = "pkcs12"
final String keystoreProvider = "SunJSSE"
final String keystoreAlias = "tomcat"

TomcatEmbeddedServletContainerFactory factory = 
        new TomcatEmbeddedServletContainerFactory(this.port);
factory.addConnectorCustomizers( new TomcatConnectorCustomizer() {
    void    customize(Connector con) {
        Http11NioProtocol proto = (Http11NioProtocol) con.getProtocolHandler();
            proto.setSSLEnabled(true);
        con.setScheme("https");
        con.setSecure(true);
        proto.setKeystoreFile(keystoreFile);
        proto.setKeystorePass(keystorePass);
        proto.setKeystoreType(keystoreType);
        proto.setProperty("keystoreProvider", keystoreProvider);
        proto.setKeyAlias(keystoreAlias);
    }
});
return factory;
}
}

The Autowiring will pick up this implementation an run with it. Once I fixed my busted keystore file (make sure you call keytool with -storetype pkcs12, not -storepass pkcs12 as reported elsewhere), this worked. Also, it would be far better to provide the parameters (port, password, etc) as configuration settings for testing and such... I'm sure its possible if you can get the @Value annotation to work with Groovy.

Dave
  • 7,589
  • 12
  • 36
  • 42
  • 3
    Thanks for looking into all this, I've been wondering about the https support in spring-boot as well. Maybe this is a limitation of spring boot that requires a bug report? HTTPS is a pretty fundamental requirement for any web application, it's strange that it's not better documented and better supported. – Jason Nov 15 '13 at 13:16
  • 2
    This is documented now in the spring boot documentation: https://github.com/spring-projects/spring-boot/blob/master/docs/howto.md – Jason Dec 12 '13 at 15:06
  • 1
    what is the difference between giving these SSL properties via application.yml and having our own implementation of TomcatConnectorCustomizer ? – yathirigan Sep 09 '15 at 09:56
0

If you don't want to implement your connector customizer, you can build and import the library (https://github.com/ycavatars/spring-boot-https-kit) which provides predefined connector customizer. According to the README, you only have to create your keystore, configure connector.https.*, import the library and add @ComponentScan("org.ycavatars.sboot.kit"). Then you'll have HTTPS connection.

Community
  • 1
  • 1
guest
  • 1
0

And here's an example of the customizer implemented in Groovy:

https://github.com/UniconLabs/orville/blob/master/web/src/main/groovy/org/apereo/openregistry/config/TomcatSslConfiguration.groovy

  • Please put the code here, since links might change, I can then upvote, since this pointed me in the right direction – Frischling Sep 20 '19 at 15:50