Https在网站中的使用不断的在普及,它不仅能够增加网站的安全性,甚至连搜索引擎都会对支持Https的网站进行区别对待。

但购买Https证书还是比较昂贵的,作为个人开发者,可以自己生成一个证书,也可以在购买域名的服务商那里免费申请到一年期的证书。像我的个人网站https://www.choupangxia.com/ 就是通过域名提供商提供的免费域名。

当然,还有一些专门的免费证书提供商提供三个月免费的证书,到期重新签名,大家感兴趣可以查找一下。

本文将以通过JDK提供的工具来生成证书,并配置到Spring Boot项目中。

证书的生成

在JDK中提供了keytool的一个证书管理工具。位于%JAVA_HOME%\bin目录下,通过该工具可生成一个免费的证书。

环境变量配置完成之后,具体生成命令如下:

  •  
keytool -genkey -alias springboot-https -keyalg RSA -keysize 2048  -keystore ./https.p12 -validity 365

上述命令对应参数作用如下:

  • genkey:指定为创密钥操作。

  • alias:指定keystore的别名。

  • keyalg:指定加密算法,这里采用RSA。

  • keysize:指定密钥长度。

  • keystore 指定生成文件位置。

  • validity 指定密钥有效期,单位为天。

当执行完命令之后,需要依次设置证书的相关信息,具体操作步骤如下:

 

  •  
MacBook-Pro:resources zzs$ keytool -genkey -alias springboot-https -keyalg RSA -keysize 2048  -keystore ./https.p12 -validity 365输入密钥库口令:再次输入新口令:您的名字与姓氏是什么?  [Unknown]:  Zhu您的组织单位名称是什么?  [Unknown]:  Bei您的组织名称是什么?  [Unknown]:  Bei您所在的城市或区域名称是什么?  [Unknown]:  BeiJing您所在的省/市/自治区名称是什么?  [Unknown]:  BeiJing该单位的双字母国家/地区代码是什么?  [Unknown]:  CNCN=Zhu, OU=Bei, O=Bei, L=BeiJing, ST=BeiJing, C=CN是否正确?  []:  是
输入 <springboot-https> 的密钥口令 (如果和密钥库口令相同, 按回车):再次输入新口令:
Warning:JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore ./https.p12 -destkeystore ./https.p12 -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。

需要注意的是在中间确认是否信息正确时,要输入中文“是”,否则会重新再让输入一遍。

经过上述命令,会在对应的文件夹中生成一个名字为https.p12的文件。

Spring Boot集成

首先,将生成的文件copy到Spring Boot项目的resources目录下。本人在创建证书时稍微讨巧一下,直接将resources目录作为输出目录,因此不用再复制了。

第二步,在application.properties对证书文件进行配置。

## 设置SSL访问端口
server.port= 8443
## 设置证书名称(用于加载对应证书)
server.ssl.key-store=https.p12
## 设置证书别名
server.ssl.key-alias=springboot-https
## 设置证书密码,创建证书时设置的密码
server.ssl.key-store-password=123456

上述配置中注释内容说明了其相应的功能,如果需要查看更多的配置项,可直接查看对应的Java类org.springframework.boot.web.server.Ssl。

第三步,创建一个简单的Controller并进行访问。Controller类如下:

 

@RestControllerpublic class HttpsController {
@RequestMapping("/") public String hello(){
return "Hello Spring-Boot-Https!"; }}

启动项目,输入日志如下:

 

INFO 117 --- [           main] com.secbro2.SpringbootHttpsApplication   : No active profile set, falling back to default profiles: defaultINFO 117 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https)INFO 117 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]INFO 117 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.29]INFO 117 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContextINFO 117 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 971 msINFO 117 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'INFO 117 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8443 (https) with context path ''INFO 117 --- [           main] com.secbro2.SpringbootHttpsApplication   : Started SpringbootHttpsApplication in 1.877 seconds (JVM running for 2.777)

通过日志可以看出,服务启动并监听在8443端口,而且在端口后面还显示请求类型为https。

第四步,访问对应的请求,这里在浏览器输入:https://localhost:8443,访问时首先会呈现如下页面信息:

SpringBoot配置Https访问_java

这是因为我们自己生成的证书并没有通过浏览器认可的机构的签名认证,因此浏览器提示风险信息。点击下面的“高级”按钮,点击“继续前往(localhost)”……

最终,页面显示效果如下: SpringBoot配置Https访问_java_02

其中在浏览器网址附近的“不安全”提醒,也是因为证书没有经过认可机构签名的原因。

http跳转Https

在上述情况下,用户在浏览器输入http://localhost:8080是没办法正常访问的。这个对照有域名的情况,比如输入https://www.choupangxia.com/可以正常访问,而输入http://www.choupangxia.com/就无法正常访问。

这种情况对用户来说是不合理的,我们应该让用户输入https://www.choupangxia.com/时也可以访问网站,只不过这中间会自动将网站跳转至Https的访问链接。

在Spring Boot中的具体配置如下:

 

  •  
@Configurationpublic class HttpsConfig {
@Bean public ConfigurableServletWebServerFactory webServerFactory() {
// 手动实例化TomcatServletWebServerFactory对象并重写其postProcessContext方法 TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() { @Override protected void postProcessContext(Context context) { // 创建安全约束组件 SecurityConstraint constraint = new SecurityConstraint(); // 设置用户约束条件,参数必须以下三种模式:NONE、INTEGRAL、CONFIDENTIAL。 // NONE表示被指定的Web资源不需要任何传输保证; // Integral表示客户机与服务器之间传送的数据在传送过程中不会被篡改; // Confidential表示数据在传送过程中被加密。 // 大多数情况下,INTEGRAL或CONFIDENTIAL是使用SSL实现。 constraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection(); collection.addPattern("/"); constraint.addCollection(collection); context.addConstraint(constraint); } };
factory.addAdditionalTomcatConnectors(httpsConnector()); return factory; }
private Connector httpsConnector() { Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); connector.setScheme("http"); // Connector监听的http的端口号 connector.setPort(8080); connector.setSecure(false); // 监听到http的端口号后转向到的https的端口号 connector.setRedirectPort(8443); return connector; }}

上述代码中主要通过重新自定义了内置的Tomcat容器,通过创建TomcatServletWebServerFactory对象,并重写其postProcessContext方法来设置安全访问约束。

同时重新定义了一个Connector,并通过@Bean注入到容器当中。

此时,启动项目,日志中关于监听端口和访问类型已经变为:

Tomcat initialized with port(s): 8443 (https) 8080 (http)
……
Tomcat started on port(s): 8443 (https) 8080 (http) with context path ''

同时支持8080端口的http和8433端口的https,同时,如果访问http://localhost:8080,最终浏览器上的地址会跳转到https://localhost:8433。

补充

上述实例中,在postProcessContext方法中通过添加constraint来约束所有的请求都进行跳转。当然,也可以针对定制化的请求设置不同的跳转方式,然后通过context.addConstraint方法多次添加。

比如,在此示例前面新增如下代码,则静态资源访问时不会进行相应的跳转:

  •  
SecurityConstraint securityConstraint = new SecurityConstraint();securityConstraint.setUserConstraint("NONE");SecurityCollection collection = new SecurityCollection();collection.addPattern("/static/*");securityConstraint.addCollection(collection);context.addConstraint(securityConstraint);

如果复制使用上述代码时注意变量名重新命名,避免冲突。

至此,关于SpringBoot项目中配置Https访问讲解完毕。