How set up Spring Boot to run HTTPS / HTTP ports

后端 未结 6 645
半阙折子戏
半阙折子戏 2020-12-12 23:58

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

相关标签:
6条回答
  • 2020-12-13 00:27

    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
      }
    }
    
    0 讨论(0)
  • 2020-12-13 00:28

    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.port=443 server.ssl.key-store=keystore.p12 server.ssl.key-store-password=password server.ssl.keyStoreType=PKCS12

    0 讨论(0)
  • 2020-12-13 00:39

    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;
    }
    
    0 讨论(0)
  • 2020-12-13 00:48

    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);
                }
            }
        };
    }
    }
    
    0 讨论(0)
  • 2020-12-13 00:50

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-13 00:51

    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

    0 讨论(0)
提交回复
热议问题