SpringBoot添加SSL证书,开启HTTPS方式(单向认证服务端)
作者:喵喵@香菜
一、前言
通过HTTP协议传输数据,并不会对数据进行加密,所以存在着一定的风险,容易被抓包破解数据,而且现在各种浏览器对使用HTTP协议的网站也会提示不安全。
通过将HTTP协议升级为HTTPS协议可以提高安全系数。使用HTTPS协议就需要了解一下SSL协议。
SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。
TLS与SSL在传输层与应用层之间对网络连接进行加密。
SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。
SSL协议可分为两层:
- SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
- SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
服务器认证阶段:
1)客户端向服务器发送一个开始信息“Hello”以便开始一个新的会话连接;
2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的“Hello”信息时将包含生成主密钥所需的信息;
3)客户根据收到的服务器响应信息,产生一个主密钥,并用服务器的公开密钥加密后传给服务器;
4)服务器恢复该主密钥,并返回给客户一个用主密钥认证的信息,以此让客户认证服务器。
用户认证阶段:
- 在此之前,服务器已经通过了客户认证,这一阶段主要完成对客户的认证。
- 经认证的服务器发送一个提问给客户,客户则返回(数字)签名后的提问和其公开密钥,从而向服务器提供认证。
SSL协议提供的安全通道有以下三个特性:
- 机密性:SSL协议使用密钥加密通信数据。
- 可靠性:服务器和客户都会被认证,客户的认证是可选的。
- 完整性:SSL协议会对传送的数据进行完整性检查。
从SSL 协议所提供的服务及其工作流程可以看出,SSL协议运行的基础是商家对消费者信息保密的承诺,这就有利于商家而不利于消费者。
在电子商务初级阶段,由于运作电子商务的企业大多是信誉较高的大公司,因此这问题还没有充分暴露出来。
但随着电子商务的发展,各中小型公司也参与进来,这样在电子支付过程中的单一认证问题就越来越突出。
虽然在SSL3.0中通过数字签名和数字证书可实现浏览器和Web服务器双方的身份验证,但是SSL协议仍存在一些问题,比如,只能提供交易中客户与服务器间的双方认证,在涉及多方的电子交易中,SSL协议并不能协调各方间的安全传输和信任关系。
在这种情况下,Visa和 MasterCard两大信用卡公司组织制定了SET协议,为网上信用卡支付提供了全球性的标准。
以上摘自百度。
二、SpringBoot中配置SSL(单向)
SSL证书的颁发必须是公开公认的CA机构颁发的,在浏览器中才会被认可是合法的;
SSL证书是针对域名的,单域名的SSL证书对非该域名也是无效的通配域名证书对一级域名和二级域名都有效。
例如:你有一个域名为 abc.com的一级域名,解析了一个.abc.com的二级域名和 b.abc.com的二级域名。当你为a.abc.com申请的单域名证书对b.abc.com域名是无效的。
(PS:我看了网上很多通过keytool生成证书的,通过keytool生成的证书是自签证书,不被浏览器锁认可)
1、环境
spring boot 2.2.2
maven
一个域名(各大域名商有售,阿里、腾讯、华为)
SSL证书(阿里云上有免费的SSL证书,有效期一年)
2、客户端单向认证服务端代码实战-JKS格式的证书
tomcat中支持JKS格式与PFX两种格式的证书,下面先进行JKS格式的证书演示。
(1)准备一个JKS后缀的SSL证书。(域名.jks)
(2)快速构建一个springboot模块
(3) 在resources下新建一个ssl目录,复制SSL证书到该目录下
(4)在项目配置文件application.yml中配置SSL
server: port: 8090 #端口配置 ssl: #ssl配置 enabled: true # 默认为true #key-alias: alias-key # 别名(可以不进行配置) # 保存SSL证书的秘钥库的路径 key-store: classpath:ssl/service.一级域名.jks key-password: 私钥密码 #key-store-password: 证书密码 key-store-type: JKS 证书类型
上述配置中 key-password 是私钥密码
key-store-password 是证书密码。
如果这两个密码相同的只配置一个即可,因为tomcat默认先用keyStore的pass去解私钥。(PS:如果你使用阿里云上的免费SSL证书,下载jks格式的证书的时候,只有一个密码,是证书的密码)
(5)添加一个controller,测试是否生效
@RestController public class TestController { @RequestMapping(value = {"","/"}) public String testHttps(){ return "you success by https"; } }
(6)本地访问 https://127.0.0.1:8090/
因为该证书对应的域名解析的服务器地址与现在访问的地址不同,所以浏览器会提示访问不安全,点击【高级】后的【继续前往…】继续跳转,发现会有很明显的不安全提示。
(7)打包,放到域名解析对应的服务器上进行测试,访问:
https://域名:8090/
可以看到当部署到域名解析对应的服务器上时,通过浏览器访问,浏览器出现一个小锁的标识。
(8)本地使用http访问: http://127.0.0.1:8090/
服务器http访问:http://域名:8090/
通过上述访问发现,如果通过http访问会提示访问需要组合TLS,但是如果用户直接通过这种方式访问的话,存在着极差的用户体验。
3、HTTP 转HTTPS
当用户使用http访问的时候,将http协议重定向到https端口
(1)修改配置文件
custom: # 自定义http启动端口 http-port: 8089 配置 server: port: 8090 #端口配置 ssl: #ssl配置 enabled: true # 默认为true #key-alias: alias-key # 别名(可以不进行配置) # 保存SSL证书的秘钥库的路径 key-store: classpath:ssl/service.一级域名.jks key-password: 私钥密码 #key-store-password: 证书密码 key-store-type: JKS
(2)添加配置类
package cn.zlc.servicehttps.config; 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.Value; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * https配置,将http请求全部转发到https * @author Jacob */ @Configuration public class HttpsConfig { @Value("${custom.http-port: 8080}") private Integer httpPort; @Value("${server.port}") private Integer port; @Bean public TomcatServletWebServerFactory servletContainer() { // 将http请求转换为https请求 TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() { @Override protected void postProcessContext(Context context) { SecurityConstraint constraint = new SecurityConstraint(); // 默认为NONE constraint.setUserConstraint("CONFIDENTIAL"); SecurityCollection collection = new SecurityCollection(); // 所有的东西都https collection.addPattern("/*"); constraint.addCollection(collection); context.addConstraint(constraint); } }; tomcat.addAdditionalTomcatConnectors(httpConnector()); return tomcat; } /** * 强制将所有的http请求转发到https * @return httpConnector */ @Bean public Connector httpConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setScheme("http"); // connector监听的http端口号 connector.setPort(httpPort); connector.setSecure(false); // 监听到http的端口号后转向到的https的端口号 connector.setRedirectPort(port); return connector; } }
(3)启动成功后可以看见http启动端口和https端口
(4)本地访问http的8089端口 http://127.0.0.1:8089/
程序会将http端口的请求转发到https端口,而用户无需做操作。
本地直接访问https端口和在服务器上直接访问就不贴图了。
4、同时开启HTTP和HTTPS
如果我们不想强制所有的请求都重定向到https或者某些功能接口需要http的支持等等,我们也可以同时开启http协议和https协议。
(1)修改配置类
package cn.zlc.servicehttps.config; 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.Value; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * https配置,将http请求全部转发到https * @author Jacob */ @Configuration public class HttpsConfig { @Value("${custom.http-port: 8080}") private Integer httpPort; @Bean public TomcatServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); tomcat.addAdditionalTomcatConnectors(httpConnector()); return tomcat; } @Bean public Connector httpConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setPort(httpPort); return connector; } }
(2)本地访问http协议的8089端口:http://127.0.0.1:8089/
(3)本地访问https的8090端口
通过对比发现,如果我们通过http端口请求的话,不会自动重定向到https端口,而且也不会提示需要TLS端口请求。
而使用https请求的时候就会提示不安全(如果是放到服务器上访问https就正常了);
5、客户端单向认证服务端代码实战-PFX格式的证书
(1)配置文件如下:
custom: http-port: 8070 server: port: 8060 #端口配置 ssl: #ssl配置 enabled: true # 默认为true #key-alias: alias-key # 别名(可以不进行配置) # 保存SSL证书的秘钥库的路径 key-store: classpath:ssl/5986342_一级域名.pfx #key-password: 私钥密码 pfx格式的使用key-password 无法正常启动 key-store-type: PKCS12 #证书类型 key-store-password: 证书密码
(2)不同点
主要的不同点就是key-store-type类型不同,同时如果是JKS的证书,使用私钥密码或者是证书密码都行,但是pfx的证书如果只配置私钥密码无法正常启动。
三、异常解决
使用jks格式的证书时,通过maven打包会出现证书内容被修改,导致证书验证失败,出现Invalid keystore format sl证书格式不合法的异常,可在maven中添加一个插件
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <configuration> <nonFilteredFileExtensions> <!--<nonFilteredFileExtension>p12</nonFilteredFileExtension>--> <nonFilteredFileExtension>jks</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin>
其它解决方案,可查看我的另一篇博客:
异常:Invalid keystore format,spring boot配置ssl证书格式不合法解决
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。