摘要:本文將介紹在 Springboot 中如何通過代碼實現 Http 到 Https 的重定向,本文僅講解 Tomcat 作爲容器的情況,其它容器將在以後一一道來。createHttpConnector() :這個方法主要是實現了在有 https 前提下,打開 http 的功能,並配置重定向的 https 的端口。

1 簡介

本文將介紹在 Springboot 中如何通過代碼實現 HttpHttps 的重定向,本文僅講解 Tomcat 作爲容器的情況,其它容器將在以後一一道來。

建議閱讀之前的相關文章:

(1) Springboot整合https原來這麼簡單

(2) HTTPS之密鑰知識與密鑰工具Keytool和Keystore-Explorer

2 相關概念

2.1 什麼叫重定向

所謂重定向,就是本來你想瀏覽地址A的,但是到達服務端後,服務端認爲地址A的界面不在了或者你沒權限訪問等原因,不想你訪問地址A;就告訴你另一個地址B,然後你再去訪問地址B。

對於重定向一般有兩個返回碼:

  • 301:永久性重定向;
  • 302:暫時性重定向。

通過 Chrome 查看網絡詳情,記錄了幾個網站的重定向情況:

網站 域名 重定向代碼 重定向後的網址
南瓜慢說 www.pkslow.com 301 https://www.pkslow.com
Google www.google.com 307 https://www.google.com
Apple www.apple.com 307 https://www.apple.com
支付寶 www.alipay.com 301 https://www.alipay.com
QQ www.qq.com 302 https://www.qq.com
百度 www.baidu.com 307 https://www.baidu.com

注:307也是重定向的一種,是新的狀態碼。

2.2 爲什麼要重定向

結合我上面特意列的表格,是不是大概想到了爲何要做這種重定向?不難發現上面的重定向都在做一件事,就是把 http 重定向爲 https 。原因如下:

(1) http 是不安全的,應該使用安全的 https 網址;

(2)但不能要求用戶每次輸入網站都輸入 https:// 吧,這也太麻煩了,所以大家都是習慣於只輸入域名,甚至連 www. 都不願意輸入。因此,用戶的輸入其實都是訪問 http 的網頁,就需要重定向到 https 以達到安全訪問的要求。

2.3 如何做到重定向

首先,服務器必須要同時支持 httphttps ,不然也就沒有重定向一說了。因爲 https 是必須提供支持的,那爲何還要提供 http 的服務呢?直接訪問 https 不就行了,不是多此一舉嗎?原因之前已經講過了,大家是習慣於只輸入簡單域名訪問的,這時到達的就是 http ,如果不提供 http 的支持,用戶還以爲你的網站已經掛了呢。

兩種協議都提供支持,所以是需要打開兩個 Socket 端口的,一般 http80 ,而 https443 。然後就需要把所有訪問 http 的請求,重定向到 https 即可。不同的服務器有不同的實現,現在介紹 Springboot+Tomcat 的實現。

3 Springboot Tomcat實現重定向

SpringbootTomcat 作爲 Servlet 容器時,有兩種方式可以實現重定向,一種是沒有使用 Spring Security 的,另一種是使用了 Spring Security 的。代碼結構如下:

主類的代碼如下:

package com.pkslow.ssl;

import com.pkslow.ssl.config.containerfactory.HttpToHttpsContainerFactoryConfig;
import com.pkslow.ssl.config.security.EnableHttpWithHttpsConfig;
import com.pkslow.ssl.config.security.HttpToHttpsWebSecurityConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@SpringBootApplication
@Import({EnableHttpWithHttpsConfig.class, HttpToHttpsWebSecurityConfig.class})
//@Import(HttpToHttpsContainerFactoryConfig.class)
@ComponentScan(basePackages = "com.pkslow.ssl.controller")
public class SpringbootSslApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootSslApplication.class, args);
    }
    
}

@ComponentScan(basePackages = "com.pkslow.ssl.controller") :沒有把 config 包掃描進來,是因爲想通過 @Import 來控制使用哪種方式來進行重定向。當然還可以使用其它方式來控制,如 @ConditionalOnProperty ,這裏就不展開講了。

當沒有使用 Spring Security 時,使用 @Import(HttpToHttpsContainerFactoryConfig.class)

當使用 Spring Security 時,使用 @Import({EnableHttpWithHttpsConfig.class, HttpToHttpsWebSecurityConfig.class})

配置文件application.properties內容如下:

server.port=443
http.port=80

server.ssl.enabled=true
server.ssl.key-store-type=jks
server.ssl.key-store=classpath:localhost.jks
server.ssl.key-store-password=changeit
server.ssl.key-alias=localhost

需要指定兩個端口, server.porthttps 端口; http.porthttp 端口。注意在沒有 https 的情況下, server.port 指的是 http 端口。

3.1 配置Container Factory實現重定向

配置的類爲 HttpToHttpsContainerFactoryConfig ,代碼如下:

package com.pkslow.ssl.config.containerfactory;

import org.apache.catalina.Context;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpToHttpsContainerFactoryConfig {
    @Value("${server.port}")
    private int httpsPort;

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

    @Bean
    public TomcatServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat =
                new TomcatServletWebServerFactory() {

                    @Override
                    protected void postProcessContext(Context context) {
                        SecurityConstraint securityConstraint = new SecurityConstraint();
                        securityConstraint.setUserConstraint("CONFIDENTIAL");
                        SecurityCollection collection = new SecurityCollection();
                        collection.addPattern("/*");
                        securityConstraint.addCollection(collection);
                        context.addConstraint(securityConstraint);
                    }
                };
        tomcat.addAdditionalTomcatConnectors(createHttpConnector());
        return tomcat;
    }

    private Connector createHttpConnector() {
        Connector connector =
                new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setScheme("http");
        connector.setSecure(false);
        connector.setPort(httpPort);
        connector.setRedirectPort(httpsPort);
        return connector;
    }
}

createHttpConnector() :這個方法主要是實現了在有 https 前提下,打開 http 的功能,並配置重定向的 https 的端口。

3.2 配置Spring security實現重定向

有兩個配置類,一個爲打開 http 服務,一個爲實現重定向。

EnableHttpWithHttpsConfig 主要作用是在已經有 https 的前提下,還要打開 http 服務。

package com.pkslow.ssl.config.security;

import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
public class EnableHttpWithHttpsConfig {
    @Value("${http.port}")
    private int httpPort;

    @Component
    public class CustomContainer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

        @Override
        public void customize(TomcatServletWebServerFactory factory) {
            Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
            connector.setPort(httpPort);
            connector.setScheme("http");
            connector.setSecure(false);
            factory.addAdditionalTomcatConnectors(connector);
        }
    }
}

HttpToHttpsWebSecurityConfig 主要是針對 Spring Security 的配置,衆所周知, Spring Security 是功能十分強大,但又很複雜的。代碼中已經寫了關鍵的註釋:

package com.pkslow.ssl.config.security;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class HttpToHttpsWebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Value("${server.port}")
    private int httpsPort;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //redirect to https - 用spring security實現
        http.portMapper().http(httpPort).mapsTo(httpsPort);
        http.requiresChannel(
                channel -> channel.anyRequest().requiresSecure()
        );

        //訪問路徑/hello不用登陸獲得權限
        http.authorizeRequests()
                .antMatchers("/hello").permitAll()
                .anyRequest().authenticated().and();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //過濾了actuator後,不會重定向,也不用權限校驗,這個功能非常有用
        web.ignoring()
                .antMatchers("/actuator")
                .antMatchers("/actuator/**");
    }
}

4 總結

最後實現了重定向,結果展示:

本文詳細代碼可在 南瓜慢說 公衆號回覆< SpringbootSSLRedirectTomcat >獲取。

參考鏈接:

Spring Security: https://docs.spring.io/spring...

Springboot 1.4重定向: https://jonaspfeifer.de/redir...

歡迎訪問 南瓜慢說 www.pkslow.com 獲取更多精彩文章!

歡迎關注微信公衆號< 南瓜慢說 >,將持續爲你更新...

相關文章