简体   繁体   English

Spring 引导 2.x - 自动将 HTTP 重定向到 HTTPS

[英]Spring Boot 2.x - Automatically redirect HTTP to HTTPS

I've been trying to find a simple answer to the following question:我一直试图找到以下问题的简单答案:
"How do I configure Spring Boot 2.x to automatically redirect requests from HTTP to HTTPS?" “如何配置 Spring Boot 2.x 以自动将请求从 HTTP 重定向到 HTTPS?”

The Spring classes involved appear to have changed from Spring Boot 1.x to 2.x, so people who have already asked/answered this question for Spring Boot 1.x have left information that no longer works in 2.x.所涉及的 Spring 类似乎已从 Spring Boot 1.x 更改为 2.x,因此已经针对 Spring Boot 1.x 中不再存在的信息提出/回答了此问题的人。 I was able to figure out the solution myself from various sources, so I'm going to answer my own question.我能够从各种来源自己找出解决方案,所以我将回答我自己的问题。 If someone else has an even cleaner solution, then I'll accept it and probably use it in my own application.如果其他人有更清洁的解决方案,那么我会接受它并可能在我自己的应用程序中使用它。

The 'challenge' in solving this problem is that there does not appear to be a simple configuration-only solution to making the embedded Tomcat servlet container listen on two ports.解决这个问题的“挑战”是似乎没有一个简单的仅配置解决方案来使嵌入式 Tomcat servlet 容器在两个端口上侦听。 If there is a way, then I haven't found it.如果有办法,那我还没有找到。 A small amount of coding was required to add a custom Connector to the TomcatServletWebServerFactory , and then configure the Connector to listen on a second port (the HTTP port) and redirect to the HTTPS port.需要进行少量编码才能将自定义Connector添加到TomcatServletWebServerFactory ,然后将连接器配置为侦听第二个端口(HTTP 端口)并重定向到 HTTPS 端口。

My solution was to implement the WebServerFactoryCustomizer interface, and add a custom Connector to the web server factory instance.我的解决方案是实现WebServerFactoryCustomizer接口,并将自定义Connector添加到 web 服务器工厂实例。

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyWebServerFactoryCustomizer.class);

    @PostConstruct
    void postConstruct() {
        LOGGER.debug("postConstruct() | INVOKED");
    }

    private final int httpPort;
    private final int redirectToHttpsPort;

    @Autowired
    MyWebServerFactoryCustomizer(
            @Value("${server.http.port}") int httpPort,
            @Value("${server.redirect.to.https.port}") int redirectToHttpsPort) {
        this.httpPort = httpPort;
        this.redirectToHttpsPort = redirectToHttpsPort;
    }

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addAdditionalTomcatConnectors(redirectConnector());
    }

    private Connector redirectConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setPort(httpPort);
        connector.setSecure(false);
        connector.setRedirectPort(redirectToHttpsPort);
        return connector;
    }

}

Inside my application.properties file, I need to provide values for the two port number, plus some other stuff related to the keystore that provides my secure HTTPS connections:在我的application.properties文件中,我需要提供两个端口号的值,以及与提供我的安全 HTTPS 连接的密钥库相关的一些其他内容:

#--------------------------------------------------------------------------------------------------
# SSL CONFIGURATION
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore/jimtough-dot-org-keystore.pkcs12
server.ssl.key-store-password=mykeystorepassword
server.ssl.key-alias=jimtough-dot-org
server.ssl.enabled=true
server.port=8443
# These two are not standard property keys. I use them to map the HTTP port to the HTTPS port.
server.http.port=8080
server.redirect.to.https.port=8443
#--------------------------------------------------------------------------------------------------

My solution also required some extra Spring Security configuration, as shown below:我的解决方案还需要一些额外的 Spring 安全配置,如下图所示:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final int httpPort;
    private final int redirectToHttpsPort;

    @Autowired
    SpringSecurityConfiguration(
            @Value("${server.http.port}") int httpPort,
            @Value("${server.redirect.to.https.port}") int redirectToHttpsPort) {
        this.httpPort = httpPort;
        this.redirectToHttpsPort = redirectToHttpsPort;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .authorizeRequests(authorize -> {
                // The following paths do not require the user to be authenticated by Spring Security
                authorize.antMatchers("/", "/favicon.ico", "/login", "/vendor/**").permitAll();
            })
            // All other request paths not covered by the list above can only be viewed by an authenticated user
            .authorizeRequests().anyRequest().authenticated()
            .and()
            .portMapper().http(httpPort).mapsTo(redirectToHttpsPort)
            .and()
            .requiresChannel().anyRequest().requiresSecure()
            .and()
            // This should redirect HTTP requests to HTTPS
            .formLogin()
            .and()
            // Use HTTP Basic authentication
            .httpBasic();
    }

}

The relevant parts above are the .portMapper().http(httpPort).mapsTo(redirectToHttpsPort) and .requiresChannel().anyRequest().requiresSecure() bits.上面的相关部分是.portMapper().http(httpPort).mapsTo(redirectToHttpsPort).requiresChannel().anyRequest().requiresSecure()位。 These should force the requester from HTTP to HTTPS automatically.这些应该会自动强制请求者从 HTTP 到 HTTPS。

Note that my solution uses three configuration properties:请注意,我的解决方案使用三个配置属性:

  • server.port
  • server.http.port
  • server.redirect.to.https.port

This can be simplified to only use two properties (just use server.port wherever I've used server.redirect.to.https.port ).这可以简化为仅使用两个属性(只要我使用server.redirect.to.https.port就使用server.port )。 I have a special case that I'm still trying to work out that required me to add the third configuration property.我有一个特殊情况,我仍在尝试解决它需要我添加第三个配置属性。 You probably don't need it.你可能不需要它。


If you're just getting started with HTTPS and Spring Boot, and you don't know how to enable HTTPS at all, then https://stackoverflow.com/a/65384432/346112 might also be of interest to you. If you're just getting started with HTTPS and Spring Boot, and you don't know how to enable HTTPS at all, then https://stackoverflow.com/a/65384432/346112 might also be of interest to you.


One more note on this.关于这一点的另一个说明。 I eventually chose to use 'nginx' as a reverse-proxy in front of my Spring Boot application on the server.我最终选择在服务器上的 Spring 引导应用程序前面使用“nginx”作为反向代理。 This moved the SSL/HTTPS configuration to a higher level, and allows nginx to terminate the HTTPS connection and forward the traffic to my Spring Boot app unencrypted.这将 SSL/HTTPS 配置提升到更高级别,并允许 nginx 终止 HTTPS 连接并将流量转发到我的 Spring 启动应用程序未加密。 I think this is a better solution.我认为这是一个更好的解决方案。 I no longer need to mess with Java keystores or any of the Spring Boot/Tomcat configuration to listen on multiple ports, forward traffic, etc. Spring Boot can just run happily on HTTP with port 8080 and let nginx deal with the rest. I no longer need to mess with Java keystores or any of the Spring Boot/Tomcat configuration to listen on multiple ports, forward traffic, etc. Spring Boot can just run happily on HTTP with port 8080 and let nginx deal with the rest. This does require a few Spring Boot settings in application.properties :这确实需要application.properties中的一些 Spring 引导设置:

server.port=8080
server.ssl.enabled=false
server.forward-headers-strategy=NATIVE
# optional
server.servlet.session.timeout=1d
server.servlet.session.cookie.max-age=1d

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM