使能 HTTPS ----- 安全的访问 Tomcat, SpringBoot
是不是看着我们的项目部署起来被浏览器地址栏标记一个
不安全
心里很不爽? 哈哈, 那就对了, 来跟帅帅一块去掉哪个不安全
吧.
1. Http
文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。
念概念好烦哦?
1.1 Http 的发展历程
1.2 Http 工作原理
HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
- 客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.luffycity.com。
- 发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
- 服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
- 释放连接TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
- 客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
念概念真的好烦哦?
1.3 Http 的特点
-
http协议是基于TCP/IP协议之上的应用层协议。
-
基于 请求-响应 的模式
-
无状态保存
-
无连接
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间,并且可以提高并发性能,不能和每个用户建立长久的连接,请求一次相应一次,服务端和客户端就中断了。但是无连接有两种方式,早期的http协议是一个请求一个响应之后,直接就断开了,但是现在的http协议1.1版本不是直接就断开了,而是等几秒钟,这几秒钟是等什么呢,等着用户有后续的操作,如果用户在这几秒钟之内有新的请求,那么还是通过之前的连接通道来收发消息,如果过了这几秒钟用户没有发送新的请求,那么就会断开连接,这样可以提高效率,减少短时间内建立连接的次数,因为建立连接也是耗时的,默认的好像是3秒中现在,但是这个时间是可以通过咱们后端的代码来调整的,自己网站根据自己网站用户的行为来分析统计出一个最优的等待时间。
1.4 HTTP 请求方法
HTTP/1.1协议中共定义了八种方法(也叫“动作”)来以不同方式操作指定的资源.
- GET
- HEAD
- POST
- PUT
- DELETE
- TRACE
- OPTIONS
- CONNECT
1.5 Http 状态码
1.6 Http 请求格式
1.7 Http 响应格式
1.8 为什么放弃 HTTP
- 很多浏览器对于不使用HTTPS协议的网站会提示不安全, 这会给用户带来一种不详的感觉,降低用户的信任度, 甚至谷歌、百度等搜索引擎巨头对于HTTPS网站都会给到更好的搜索排名
- 网络运营商可以任意劫持我们的流量插入自己的广告
- 最重要的还是安全,HTTP协议使用明文传输,这就意味着只要原意,你网站传输的数据都可以被传输设备截取,修改.
2. Https 简介
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性
https把http消息进行加密之后再传送,这样就算坏人拦截到了,得到消息之后也看不懂,这样就做到了安全,具体来说,https是通过对称加密和非对称加密和hash算法共同作用,来在性能和安全性上达到一个平衡,加密是会影响性能的,尤其是非对称加密,因为它的算法比较复杂,那么加密了就安全了吗?不是的,https除了对消息进行了加密以外还会对通信的对象进行身份验证。
https并不是一种新的协议,而是使用了一种叫做TLS(Transport layer secure)的安全层,这个安全层提供了数据加密的支持,让http消息运行在这个安全层上,就达到了安全,而运行在这个安全层上的http就叫做https(http secure)
2.1 HTTP 与 HTTPS 的区别
- HTTP 是明文传输协议,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全
- HTTPS比HTTP更加安全,对搜索引擎更友好,利于SEO,谷歌、百度优先索引HTTPS网页;
- HTTPS需要用到SSL证书,而HTTP不用;
- HTTPS标准端口443,HTTP标准端口80;
- HTTPS基于传输层,HTTP基于应用层;
- HTTPS在浏览器显示绿色安全锁(认证的证书),HTTP显示不安全;
2.2 为何不所有的网站都使用HTTPS
既然HTTPS那么安全可靠,那为何不所有的Web网站都使用HTTPS?
- HTTPS实施有门槛,这个门槛在于需要权威CA颁发的SSL证书。从证书的选择、购买到部署,传统的模式下都会比较耗时耗力
- HTTPS普遍认为性能消耗要大于HTTP,因为与纯文本通信相比,加密通信会消耗更多的CPU及内存资源。如果每次通信都加密,会消耗相当多的资源,平摊到一台计算机上时,能够处理的请求数量必定也会随之减少。
但事实并非如此,用户可以通过性能优化、把证书部署在SLB或CDN,来解决此问题。举个实际的例子,“双十一”期间,全站HTTPS的淘宝、天猫依然保证了网站和移动端的访问、浏览、交易等操作的顺畅、平滑。通过测试发现,经过优化后的许多页面性能与HTTP持平,因此HTTPS经过优化之后其实并不慢。
- 想要节约购买证书的开销也是原因之一。要进行HTTPS通信,证书是必不可少的。而使用的证书必须向认证机构(CA)购买。
- 最后是安全意识。相比国内,国外互联网行业的安全意识和技术应用相对成熟,HTTPS部署趋势是由社会、企业、政府共同去推动的.
念概念真的真的好烦哦?
3. Https 工作原理
HTTPS 协议的主要功能基本都依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法:散列函数 、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性。
3.1 对称加密算法
对称加密(也叫私钥加密),是指加密和解密使用相同的密钥的加密算法。它的缺点是加密和解密的密钥相同,在传输过程中容易被获取密钥。
3.2 非对称加密算法
非对称加密,是指加解密算法不同,非对称加密算法有两个密钥:公开密钥(public key)和私有密钥(private key);并且加密密钥和解密密钥是成对出现的。公钥对数据进行加密且它是公开的,私钥对数据进行解密且它是私有的。因此在传入过程中即便被获取了公钥也无法获解密该密文数据。
3.3 Https 工作流程
- Client发起一个HTTPS(比如
https://juejin.im/user/5a9a9cdcf265da238b7d771c
)的请求,根据RFC2818的规定,Client知道需要连接Server的443(默认)端口。 - Server把事先配置好的公钥证书(public key certificate)返回给客户端.(任何人(包括黑客)访问服务器端都可以把公钥证书给它, 因此该证书才称为公钥证书)
- Client验证公钥证书:比如是否在有效期内,证书的用途是不是匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效,这是一个递归的过程,直到验证到根证书(操作系统内置的Root证书或者Client内置的Root证书)。如果验证通过则继续,不通过则显示警告信息(比如我们用 keygen 自己生成的证书就不被浏览器认可, 会弹出警告)。
- Client使用伪随机数生成器生成加密所使用的对称密钥,然后用证书的公钥加密这个对称密钥,发给Server。
- Server使用自己的私钥(private key)解密这个消息,得到对称密钥。至此,Client和Server双方都持有了相同的对称密钥。
- Server使用对称密钥加密“明文内容A”,发送给Client。
- Client使用对称密钥解密响应的密文,得到“明文内容A”。
- Client再次发起HTTPS的请求,使用对称密钥加密请求的“明文内容B”,然后Server使用对称密钥解密密文,得到“明文内容B”。
我发誓: 我一定不照着小本本念概念了, 哼…
4. Tomcat 配置 Https 连接
官方文档: http://localhost:8080/docs/ssl-howto.html#Configuration
4.1 生成数字证书
C:\Users\Administrator>keytool -genkey -alias angboot -keyalg RSA -keystore E://angboot.keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: inetsoft
What is the name of your organization?
[Unknown]: inetsoft
What is the name of your City or Locality?
[Unknown]: Xi'an
What is the name of your State or Province?
[Unknown]: Shan'xi
What is the two-letter country code for this unit?
[Unknown]: 86
Is CN=localhost, OU=inetsoft, O=inetsoft, L=Xi'an, ST=Shan'xi, C=86 correct?
[no]: yes
Enter key password for <angboot>
(RETURN if same as keystore password):
Re-enter new password:
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore E://angboot.keystore -destkeystore E://angboot.keystore -deststoretype pkcs12".
keytool -genkeypair 命令介绍:
-genkeypair:生成一对非对称密钥;
-alias:指定密钥对的别名,该别名是公开的;
-keyalg:指定加密算法,本例中的采用通用的RAS加密算法;
-keystore:密钥库的路径及名称,不指定的话,默认在操作系统的用户目录下生成一个".keystore"的文件;
-validity:设置秘钥有效期,单位为天,36500即为100年;
注意
-
密钥库的密码至少必须6个字符,可以是纯数字或者字母或者数字和字母的组合等等
-
名字与姓氏 (CN)
应该是输入域名,而不是我们的个人姓名,本机可用localhost,其他的可以不填
4.2 配置 server.xml
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/angboot.keystore"
type="RSA" certificateKeystorePassword="angboot"/>
</SSLHostConfig>
</Connector>
4.3 访问测试
-
8080 Http 依然可以访问
-
8443 HTTPS 访问
为什么 HTTPS 访问浏览器还是显示 不安全
连接字样?
因为自签数字证书是不被浏览器认可的, 所以依然会提示
不安全
(怪就怪帅帅穷, 没钱去 CA 机构买证书).
如果特别穷, 但同时也特别不想看到不安全
那几个字样, 也可以配置本机让自己的浏览器认可自签证书,
See: 使用自签名SSL证书配置HTTPS,解决浏览器提示不安全警告
4.4 配置 HTTP 自动跳转到 HTTPS
修改
{tomcat.home}/conf/web.xml
文件, 添加 SSL 配置
....
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<login-config>
<!-- Authorization setting for SSL -->
<auth-method>CLIENT-CERT</auth-method>
<realm-name>Client Cert Users-only Area</realm-name>
</login-config>
<security-constraint>
<!-- Authorization setting for SSL -->
<web-resource-collection >
<web-resource-name >SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
刷新 http://localhost:8080
就会跳转到 https://localhost:8443
5. SpringBoot 支持 Https
SpringBoot 项目可以在 applicaton.properties[.yml] 文件中配置, 也可以通过 Java 代码配置类的方式去配置, 原理一样, 但是像帅帅这么优秀的人当然需要同时支持 HTTP 和 HTTPS, 并且还要能随意切换(嗯哼? 你咋不上天呢?), 就需要 Java 配置类的方式去配置, 而且 application.properties[.yml] 文件中不能有任何 tomcat 的配置(比如:server.port, 坑了我半个小时), 否则结果很玄幻.
话不多说, 直接撸代码
- AngBootServerConfiguration 提供 HTTP 访问, 并支持自定义配置
package org.angboot.config;
import org.angboot.constants.security.SecurityConstant;
import org.angboot.util.AngBootEnv;
import org.angboot.util.conditional.ConditionalOnHttpsDisabled;
import org.apache.catalina.Context;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.*;
@Configuration
@Conditional(ConditionalOnHttpsDisabled.class)
public class AngBootServerConfiguration {
@Bean
@Profile("prod")
public TomcatServletWebServerFactory prodTomcatServletWebServerFactory(){
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.setPort(SecurityConstant.DEFAULT_HTTP_PORT);
return tomcat;
}
@Bean
@Profile("dev")
public TomcatServletWebServerFactory devTomcatServletWebServerFactory(){
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.setPort(SecurityConstant.DEFAULT_DEVELOP_HTTP_PORT);
return tomcat;
}
}
- AngBootHttpsConfiguration: 配置 HTTPS 访问.
package org.angboot.config;
import org.angboot.constants.security.SecurityConstant;
import org.angboot.util.AngBootEnv;
import org.angboot.util.conditional.ConditionalOnHttpsEnabled;
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.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.Ssl;
import org.springframework.context.annotation.*;
@Configuration
@Conditional(ConditionalOnHttpsEnabled.class)
public class AngBootHttpsConfiguration {
/**
* Redirect http connection to https connection
*/
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(){
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(connector());
// setting server.port
tomcat.setPort(AngBootEnv.getIntegerProperty(SecurityConstant.HTTPS_PORT_KEY,
SecurityConstant.DEFAULT_HTTPS_PORT));
// setting ssl
tomcat.setSsl(getSsl());
return tomcat;
}
@Bean
public Connector connector(){
Connector connector = new Connector();
connector.setScheme("http"); // default
connector.setPort(SecurityConstant.DEFAULT_HTTP_PORT);
connector.setSecure(false);
connector.setRedirectPort(AngBootEnv.getIntegerProperty(SecurityConstant.HTTPS_PORT_KEY,
SecurityConstant.DEFAULT_HTTPS_PORT));
return connector;
}
@Bean
public Ssl getSsl() {
Ssl ssl = new Ssl();
ssl.setEnabled(true);
ssl.setKeyStore(getBasedSslFolder() + AngBootEnv.getProperty(SecurityConstant.HTTPS_KEY_STORE_PATH_KEY));
ssl.setKeyAlias(AngBootEnv.getProperty(SecurityConstant.HTTPS_KEY_ALIAS_KEY));
ssl.setKeyStorePassword(AngBootEnv.getProperty(SecurityConstant.HTTPS_KEY_STORE_PASSWORD_KEY));
ssl.setKeyStoreType(AngBootEnv.getProperty(SecurityConstant.HTTPS_KEY_STORE_TYPE_KEY));
return ssl;
}
/**
* Get ssl config base folder.
*/
private String getBasedSslFolder() {
return AngBootEnv.getHome() + ANGBOOT_SSL_BASED_PATH;
}
public static final String ANGBOOT_SSL_BASED_PATH = "/ssl/";
}
- ConditionalOnHttpsEnabled.java
package org.angboot.util.conditional;
import org.angboot.constants.security.SecurityConstant;
import org.angboot.util.AngBootEnv;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.stereotype.Component;
@Component
public class ConditionalOnHttpsEnabled extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean isHttpsEnabled = AngBootEnv.getBoolean(SecurityConstant.HTTPS_ENABLED_KEY);
return new ConditionOutcome(isHttpsEnabled, "https enabled: " + isHttpsEnabled);
}
}
- 对应代码配置:
angboot.https.enabled=true
angboot.https.port=443
angboot.https.ssl.key.store.path=angboot.keystore
angboot.https.ssl.key.alias=angboot
angboot.https.ssl.key.store.pwd=angboot
angboot.https.ssl.key.store.type=JKS
过几天, 我的项目 AngBoot: https://github.com/DreamLi1314/angboot 部署上云服务器就看不到哪个
不安全
咯, 抓紧时间再去看几眼吧.哈哈哈…