Https系列之三:让服务器同时支持http、https,基于spring boot

Https系列会在下面几篇文章中分别作介绍:

一:https的简单介绍及SSL证书的生成
二:https的SSL证书在服务器端的部署,基于tomcat,spring boot
三:让服务器同时支持http、https,基于spring boot
四:https的SSL证书在Android端基于okhttp,Retrofit的使用

所有文章会优先在:
微信公众号“颜家大少”中发布
转载请标明出处


前面已介绍了:”https在服务器端的部署,基于tomcat,spring boot
但我们会发现一个问题,只能用https登录我们的网站,而不能用http

假设我们的网站名为:www.my.com
如果是之前的http,我们只需在浏览器中输入:my.com
浏览器就会自动登录到:http:// www.my.com
但部署了https后,发现在浏览器中输入:my.com,返回的结果是:无法访问此网站
这对用户来说,体验是非常不好的。

好吧,那我们试试一些比较有名的网站,如阿里云。
在浏览器中输入:aliyun.com
就能自动跳转到:https: //www.aliyun.com

那我们能不能在部署了https后,在输入:my.com
自动跳转到https对应的: https:// www.my.com

依然跳转到:http:// www.my.com ?

答案是,上面两种方法都可以的,任君选择

下面介绍的就是以上要求基于spring boot的实现

直接上代码:

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ServerMain implements CommandLineRunner{    
    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                //Due to CONFIDENTIAL and /*, this will cause Tomcat to redirect every request to HTTPS. 
                //You can configure multiple patterns and multiple constraints if you need more control over what is and is not redirected.

                SecurityConstraint constraint = new SecurityConstraint();
                constraint.setUserConstraint("CONFIDENTIAL");
                SecurityCollection collection = new SecurityCollection();
                collection.addPattern("/*");
                constraint.addCollection(collection);
                context.addConstraint(constraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(httpConnector());
        return tomcat;

  }
    @Bean
    public Connector httpConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");

        //Set the scheme that will be assigned to requests received through this connector
        //@param scheme The new scheme
        connector.setScheme("http");

        //Set the port number on which we listen for requests.
        // @param port The new port number
        connector.setPort(80);       

        //Set the secure connection flag that will be assigned to requests received through this connector.
        //@param secure The new secure connection flag
        //if connector.setSecure(true),the http use the http and https use the https;else if connector.setSecure(false),the http redirect to https;
        connector.setSecure(false);

        //redirectPort The redirect port number (non-SSL to SSL)
        connector.setRedirectPort(443);
        return connector;
    }

    public static void main(String[] args) throws Exception {            
        SpringApplication.run(ServerMain.class, args);
    }
    @Override
    public void run(String... arg0) throws Exception {
        // TODO Auto-generated method stub
    }

}

其中,下面代码的作用是把此EmbeddedServletContainerFactory 注入到web容器中

@Bean
public EmbeddedServletContainerFactory servletContainer()

然后,用下面的代码拦截所有的/*请求

@Override
protected void postProcessContext(Context context) {
..........
    constraint.setUserConstraint("CONFIDENTIAL");
    collection.addPattern("/*");
.............
}

并把其关联到下面的httpConnector中

@Bean
public Connector httpConnector()

最后,在public Connector httpConnector()中,
把http设为默认的80端口,并把http的请求跳转到443的https端口
其中443是https的默认端口,也可以设为其它的值,但要和resources/application.properties的内容对应
如下:

server.port=443
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=123456
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias:tomcat

运行服务器,会看到打印如下:
这里写图片描述
其中会看到TomcatEmbeddedServletContainer,和同时开启的两个端口:443 (https) 80 (http)

TomcatEmbeddedServletContainer : Tomcat started on port(s): 443 (https) 80 (http)

Ok,那现在试试输入:my.com,就会发现浏览器会直接跳到:https:// www.my.com了

到此,这件事情就算是大功告成了。

但此时有同学可能会提出特殊的要求:
他的https只是为了某某的要求而使用的,比如说要接入什么什么的一定要填的是https的地址
而他的网站根本就不需要https这种安全级别的,另外,他觉得http的访问速度可能会快点,你知到有些同学是有这种洁癖的 :p
也就是说:
输入:my.com,跳到: http:// www.my.com
输入:https:// www.my.com,跳到:https:// www.my.com
要实现此要求,其实很简单,只需要把:

@Bean
public Connector httpConnector() {
........
connector.setSecure(false);
...........

改为

@Bean
public Connector httpConnector() {
........
connector.setSecure(true);
...........

就大功告成了
更多内容请看:Https系列之四:https的SSL证书在Android端基于okhttp,Retrofit的使用


更多内容,请关注微信公众号:颜家大少
这里写图片描述

posted @ 2017-09-27 10:21 颜家大少 阅读(...) 评论(...) 编辑 收藏