How set up Spring Boot to run HTTPS / HTTP ports

JavaSpringSpring BootSsl

Java Problem Overview


Spring boot have some properties to config web port and SSL settings, but once a SSL certificate is set the http port turns into https port.

So, how can I keep both ports running on it, for example: 80 an 443 at the same time?

As you can see, there only properties for one port, in this case "server.ssl" is enabled, what makes http port be disabled automatically.

##############
### Server ###
##############
server.port=9043
server.session-timeout=1800
server.ssl.key-store=file:///C:/Temp/config/localhost.jks
server.ssl.key-store-password=localhost
server.ssl.key-password=localhost
server.ssl.trust-store=file:///C:/Temp/config/localhost.jks
server.ssl.trust-store-password=localhost

I am trying to use even Tomcat or Undertow. I'd appreciate any help!

Java Solutions


Solution 1 - Java

Spring Boot configuration using properties, allows configuring only one connector. What you need is multiple connectors and for this, you have to write a Configuration class. Follow instructions in

https://docs.spring.io/spring-boot/docs/1.2.3.RELEASE/reference/html/howto-embedded-servlet-containers.html

You can find a working example of configuring HTTPS through properties and then HTTP though EmbeddedServletContainerCustomizer below

http://izeye.blogspot.com/2015/01/configure-http-and-https-in-spring-boot.html?showComment=1461632100718#c4988529876932015554

server:
  port: 8080
  ssl:
    enabled: true
    keyStoreType: PKCS12
    key-store: /path/to/keystore.p12
    key-store-password: password
  http:
    port: 8079

@Configuration
public class TomcatConfig {

@Value("${server.http.port}")
private int httpPort;

@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                TomcatEmbeddedServletContainerFactory containerFactory =
                        (TomcatEmbeddedServletContainerFactory) container;

                Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
                connector.setPort(httpPort);
                containerFactory.addAdditionalTomcatConnectors(connector);
            }
        }
    };
}
}

Solution 2 - Java

The currently accepted answer works perfectly but needs some adaption if you want it to work with Spring Boot 2.0.0 and onwards:

@Component
public class HttpServer {
  @Bean
  public ServletWebServerFactory servletContainer(@Value("${server.http.port}") int httpPort) {
      Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
      connector.setPort(httpPort);

      TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
      tomcat.addAdditionalTomcatConnectors(connector);
      return tomcat;
  }
}

or the Kotlin version:

@Component
class HttpServer {
  @Bean
  fun servletContainer(@Value("\${server.http.port}") httpPort: Int): ServletWebServerFactory {
    val connector = Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL)
    connector.setPort(httpPort)

    val tomcat = TomcatServletWebServerFactory()
    tomcat.addAdditionalTomcatConnectors(connector)
    return tomcat
  }
}

Solution 3 - Java

Bellow is a simple example of how to enable both HTTP/HTTPS ports for undertow.

Spring Boot only lets to open one port by configuration. Second port has to be opened programmatically.

###Open HTTP port first programmatically.

import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;

@Configuration
public class UndertowConfig {

@Value("${server.http.port}")
private int httpPort;

@Value("${server.http.interface}")
private String httpInterface;

@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> containerCustomizer() {
    return (WebServerFactoryCustomizer) factory -> {
        UndertowServletWebServerFactory undertowFactory = (UndertowServletWebServerFactory) factory;
        undertowFactory.getBuilderCustomizers().add(builder -> {
            builder.addHttpListener(httpPort, httpInterface);
        });
    };
}

}

#HTTPS by configuration Spring can open one either HTTP or HTTPS port reading properties from an available property source. If you add appropriate configuration as shown bellow it would be good enough to have HTTPs port open.

#default secured port (Spring will open it automatically)
server.port=8443
#additional HTTP port (will open it in UndertowConfig)
server.http.port=8080
#Open to the world
server.http.interface=0.0.0.0
#These settings tell Spring to open SSL port
server.ssl.keystore=file:${APP_BASE}/conf/server/ssl_selfsigned/server.keystore
server.ssl.key-store-password=xyz
server.ssl.key-password=xyz

###HTTPS by manual setup You can open another SSL port the same way as you opened HTTP port if you want by doing this

 .addHttpsListener(ssl_port, httpInterface, getSSLContext());

This is how you can create SSL context

import javax.net.ssl.*;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;

public SSLContext getSSLContext() throws Exception
{
    return createSSLContext(loadKeyStore(serverKeystore,keyStorePassword),
            loadKeyStore(serverTruststore,trustStorePassword));

}


private SSLContext createSSLContext(final KeyStore keyStore,
                                    final KeyStore trustStore) throws Exception {

    KeyManager[] keyManagers;
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
    keyManagers = keyManagerFactory.getKeyManagers();

    TrustManager[] trustManagers;
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(trustStore);
    trustManagers = trustManagerFactory.getTrustManagers();

    SSLContext sslContext;
    sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keyManagers, trustManagers, null);

    return sslContext;
}


private static KeyStore loadKeyStore(final String storeLoc, final String storePw) throws Exception {
    InputStream stream = Files.newInputStream(Paths.get(storeLoc));
    if(stream == null) {
        throw new IllegalArgumentException("Could not load keystore");
    }
    try(InputStream is = stream) {
        KeyStore loadedKeystore = KeyStore.getInstance("JKS");
        loadedKeystore.load(is, storePw.toCharArray());
        return loadedKeystore;
    }
}

Solution 4 - Java

Another Spring boot 2.x solution:

private static final int HTTP_PORT = 80;
private static final int HTTPS_PORT = 443;
private static final String HTTP = "http";
private static final String USER_CONSTRAINT = "CONFIDENTIAL";

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint(USER_CONSTRAINT);
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    tomcat.addAdditionalTomcatConnectors(redirectConnector());
    return tomcat;
}

private Connector redirectConnector() {
    Connector connector = new Connector(
            TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
    connector.setScheme(HTTP);
    connector.setPort(HTTP_PORT);
    connector.setSecure(false);
    connector.setRedirectPort(HTTPS_PORT);
    return connector;
}

And set in your properties server.port=443

Solution 5 - Java

Take a look at: https://github.com/creactiviti/spring-boot-starter-acme. It makes it very easy to auto-generate a LetsEncrypt based SSL certificate.

From the README:

  1. Add the module to your pom.xml file as a dependency.

  2. Build your project.

  3. Deploy it to a target machine and point your domain name to the IP address of that machine. LetsEncrypt validates your ownership of the domain by making a callback to the http://your-domain/.well-known/acme-challenge/{token} endpoint exposed by this module.

  4. Make sure that your server has openssl available on its $PATH.

  5. To activate spring-boot-starter-acme and generate a certificate execute:

    sudo java -Dserver.port=80 -Dacme.enabled=true -Dacme.domain-name=<YOUR_DOMAIN_NAME> -Dacme.accept-terms-of-service=true -jar mysecureapp-0.0.1-SNAPSHOT.jar

  6. Check your console for a confirmation that the certificate was successfully generated.

  7. Stop your application and configure it to make use of the generated certificate:

    server.ssl.key-store=keystore.p12
    server.ssl.key-store-password=password
    server.ssl.keyStoreType=PKCS12```
    

Solution 6 - Java

The top answers are all great and probably work but I've been using Undertow with JHipster so they didn't work for me (and this was the main search result). The right code for Undertow is mentioned in this issue specifically:

@Bean
public UndertowServletWebServerFactory embeddedServletContainerFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
        @Override
        public void customize(Undertow.Builder builder) {
            builder.addHttpListener(8080, "0.0.0.0");
        }
    });
    return factory;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCarlos AlbertoView Question on Stackoverflow
Solution 1 - JavaHarish GokavarapuView Answer on Stackoverflow
Solution 2 - JavaNickDKView Answer on Stackoverflow
Solution 3 - JavaStan SokolovView Answer on Stackoverflow
Solution 4 - JavamaxView Answer on Stackoverflow
Solution 5 - JavaokrunnerView Answer on Stackoverflow
Solution 6 - JavaShai AlmogView Answer on Stackoverflow