1 简介
本文将介绍在 Springboot 中如何通过代码实现 Http 到 Https 的重定向, 本文仅讲解 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/ |
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:
Springboot 1.4 重定向:
来源: http://www.bubuko.com/infodetail-3528454.html