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,访问时首先会呈现如下页面信息:
其中在浏览器网址附近的“不安全”提醒,也是因为证书没有经过认可机构签名的原因。
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访问讲解完毕。