Spring Boot 2: How to enable two-way SSL with embedded Tomcat

In Spring Boot 2, the configuration classes of embedded webservers (like Tomcat) have slightly changed.

Below is a way to enable a two-way SSL connector on Tomcat.

Versions used:

  • Spring Boot 2.0.4.RELEASE
  • Embedded Tomcat version: 8.5.32
package re.vianneyfaiv.twowayssl;

import org.apache.catalina.connector.Connector;
import org.apache.commons.lang3.StringUtils;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.nio.file.Paths;

@Configuration
public class TomcatWithTwoWaySslConfig {

    @Autowired
    private ServerProperties serverProperties;

    private int port = 9090;

    private String keystoreFile = "/absolute/path/to/keystore";
    private String keystorePassword = "keystore-password";

    private String truststoreFile = "/absolute/path/to/truststore";
    private String truststorePassword = "truststore-password";

    @Bean
    public TomcatServletWebServerFactory servletContainer() {

        // Creates default Spring Boot Tomcat config with default HTTP(s) Connector based on ServerProperties (server.* props)
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(serverProperties.getPort());

        // Defines application context path (if set)
        if(StringUtils.isNotBlank(serverProperties.getServlet().getContextPath())) {
            tomcat.setContextPath(serverProperties.getServlet().getContextPath());
        }

        // Creation of a new HTTPS connector
        tomcat.addAdditionalTomcatConnectors(twoWaySSL(port, keystoreFile, keystorePassword, truststoreFile, truststorePassword));

        return tomcat;
    }

    private static Connector twoWaySSL(int port,
                                       String keystoreFile, String keystorePassword,
                                       String truststoreFile, String truststorePassword) {

        // Create HTTP connector
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(port);
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();

        // Enable SSL
        connector.setScheme("https");
        connector.setSecure(true);
        protocol.setSSLEnabled(true);

        // Keystore config
        File keystore = Paths.get(keystoreFile).toFile();
        protocol.setKeystoreFile(keystore.getAbsolutePath());
        protocol.setKeystorePass(keystorePassword);

        // Truststore config
        File truststore = Paths.get(truststoreFile).toFile();
        protocol.setTruststoreFile(truststore.getAbsolutePath());
        protocol.setTruststorePass(truststorePassword);

        // Enable two way SSL
        protocol.setClientAuth(SSLHostConfig.CertificateVerification.REQUIRED.name());
        protocol.setSSLVerifyClient(SSLHostConfig.CertificateVerification.REQUIRED.name());

        return connector;
    }
}

Let me know if that does not work for you.

Spring security: how to ignore some paths

Here is the way to configure Spring Security to tell him to ignore some paths. It could be useful when you need to have public APIs or make static resources public.

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Override
 	public void configure(WebSecurity web) throws Exception {
 		web.ignoring().antMatchers("/static/**");
 	}
}

Spring boot: 1.3.3.RELEASE
Spring Security: 4.0.3.RELEASE