Springboot以Tomcat爲容器實現http重定向到https的兩種方式
摘要:本文將介紹在 Springboot 中如何通過代碼實現 Http 到 Https 的重定向,本文僅講解 Tomcat 作爲容器的情況,其它容器將在以後一一道來。createHttpConnector() :這個方法主要是實現了在有 https 前提下,打開 http 的功能,並配置重定向的 https 的端口。
1 簡介
本文將介紹在 Springboot
中如何通過代碼實現 Http
到 Https
的重定向,本文僅講解 Tomcat
作爲容器的情況,其它容器將在以後一一道來。
建議閱讀之前的相關文章:
(2) HTTPS之密鑰知識與密鑰工具Keytool和Keystore-Explorer
2 相關概念
2.1 什麼叫重定向
所謂重定向,就是本來你想瀏覽地址A的,但是到達服務端後,服務端認爲地址A的界面不在了或者你沒權限訪問等原因,不想你訪問地址A;就告訴你另一個地址B,然後你再去訪問地址B。
對於重定向一般有兩個返回碼:
- 301:永久性重定向;
- 302:暫時性重定向。
通過 Chrome
查看網絡詳情,記錄了幾個網站的重定向情況:
網站 | 域名 | 重定向代碼 | 重定向後的網址 |
---|---|---|---|
南瓜慢說 | www.pkslow.com | 301 | https://www.pkslow.com |
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 |
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 如何做到重定向
首先,服務器必須要同時支持 http
和 https
,不然也就沒有重定向一說了。因爲 https
是必須提供支持的,那爲何還要提供 http
的服務呢?直接訪問 https
不就行了,不是多此一舉嗎?原因之前已經講過了,大家是習慣於只輸入簡單域名訪問的,這時到達的就是 http
,如果不提供 http
的支持,用戶還以爲你的網站已經掛了呢。
兩種協議都提供支持,所以是需要打開兩個 Socket
端口的,一般 http
爲 80
,而 https
爲 443
。然後就需要把所有訪問 http
的請求,重定向到 https
即可。不同的服務器有不同的實現,現在介紹 Springboot+Tomcat
的實現。
3 Springboot Tomcat實現重定向
Springboot
以 Tomcat
作爲 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.port
爲 https
端口; http.port
爲 http
端口。注意在沒有 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 獲取更多精彩文章!
歡迎關注微信公衆號< 南瓜慢說 >,將持續爲你更新...