目录:
按照官方的说法,Spring Boot无法使用配置同时支持http和https两种协议访问应用。替代方案是可以配置一种协议,然后另外一种协议的支持,通过编码来实现。推荐的方案是使用配置支持https,而写代码支持http,原因是编码写http会比较复杂一些。官方的samples示例中演示了如果通过代码支持https(也就是官方认为比较复杂的那种情况。)
下面我们来使用另外一种实现方案,就是通过配置支持https,通过编码支持http。
1. 生成并安装证书
首先生成SSL证书,这里选择P12格式。
1 | keytool -genkey -alias xgsdk -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 |
按提示输入各项之后,可以得到一个keystore.p12文件。可以通过以下命令验证其中的信息。
1 | keytool -list -v -keystore keystore.p12 -storetype pkcs12 |
如果ssl链接仅通过浏览器访问,可以跳过下面这步,如果需要通过客户端访问,可以从浏览器中导出cer或者crt格式的公钥,然后通过以下命令添加证书到信任列表。
1 | keytool -import -alias xgsdk -keystore $JAVA_HOME/jre/lib/security/cacerts -file xgsdk.com.crt
|
2. 配置SSL支持
将keystore.p12放到工程根目录下,并在application.properties文件配置即可:
1 2 3 4 | server.ssl.key-store = keystore.p12 server.ssl.key-store-password = your_password server.ssl.keyStoreType = PKCS12 server.ssl.keyAlias = xgsdk |
还可以额外定义端口,例如到8443:
1 | server.port=8443 |
此时服务器已经打开8443端口,需要用https访问。访问http端口会发现已经不通了。
3. 配置HTTP支持
接下来通过编码的方式支持HTTP协议访问,可以在application.java入口中加入以下代码创建一个新德Connector:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Value("${xgsdk.http.port}") private int httpPort; @Bean public EmbeddedServletContainerFactory servletContainer() { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory(); tomcat.addAdditionalTomcatConnectors(createHttpConnector()); return tomcat; } private Connector createHttpConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setScheme("http"); connector.setPort(httpPort); connector.setSecure(false); return connector; } |
另外在application.properties文件配置http端口即可:
1 | xgsdk.http.port=8090 |
此时系统已经完全支持http和https同时访问。
4. 配置HTTP自动跳转到HTTPS
我们希望用户访问http端口的时候会自动跳转到https协议来访问,可以在创建connect的时候进行端口重定向。(当然还有其他方式,比如可以在应用中通过filter进行重定向处理)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Bean public EmbeddedServletContainerFactory servletContainer() { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() { 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("org.apache.coyote.http11.Http11NioProtocol"); connector.setScheme("http"); connector.setPort(httpPort); connector.setSecure(false); connector.setRedirectPort(httpsPort); return connector; } |
5. 配置部分链接允许http访问
这个需求源自一些静态资源,使用http协议访问可以获得更高效率。这里需要再增加一个SecurityConstraint对象进行处理,我们先设置一些url-pattern,然后将这些pattern加入到NONE策略的SecurityConstraint中,以便允许这部分链接通过http访问。而剩下的仍然走CONFIDENTIAL策略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | private static final String HTTP_URL_PATTERNS[] = { "/static/*", "/download/*", "/doc/*" }; @Bean public EmbeddedServletContainerFactory servletContainer() { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() { @Override protected void postProcessContext(Context context) { SecurityConstraint securityConstraint = new SecurityConstraint(); securityConstraint.setUserConstraint("NONE"); SecurityCollection collection = new SecurityCollection(); for (String pattern : HTTP_URL_PATTERNS) { collection.addPattern(pattern); } securityConstraint.addCollection(collection); context.addConstraint(securityConstraint); SecurityConstraint securityConstraint2 = new SecurityConstraint(); securityConstraint2.setUserConstraint("CONFIDENTIAL"); SecurityCollection collection2 = new SecurityCollection(); collection2.addPattern("/"); securityConstraint2.addCollection(collection2); context.addConstraint(securityConstraint2); LOGGER.info("Constraints length = " + context.findConstraints().length); } }; tomcat.addAdditionalTomcatConnectors(createHttpConnector()); return tomcat; } private Connector createHttpConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setScheme("http"); connector.setPort(httpPort); connector.setSecure(false); connector.setRedirectPort(httpsPort); return connector; } |