http随笔
一、http报文结构和方法
http请求报文格式,分为三部分:开始行(请求行)+ 首部(请求头)+ 实体(请求体)。可以看到,开始行中包含请求方法 + 请求路径 + HTTP版本。请求头之后有一个空行表示头的结束,请求实体里可以是表单数据或JSON数据等。


http应答报文格式也分为三部分:状态行 + 首部(响应头)+ 实体(响应体)

以下为http请求、响应报文结构:

可以看到,HTTP报文中包括Start Line、Header、Body三部分,其中第一行为Start Line(请求报文为请求行,应答报文为状态行),Header中每条内容也以换行结束,当遇到一个空行(两个/r/n)就表示Header的结束,Body的开始。
URL的格式:

通常浏览器通过URL获取的第一个资源是HTML网页,在网页中,如果嵌入了JavaScript、CSS、图片、视频等其他资源,浏览器会根据资源的URL再次向服务器请求对应的资源。
http1.1中请求的方法有:
GET:请求资源 / RESTful API中的查询数据。
POST:向服务器传输实体的主体 / RESTful API中的增加数据。
PUT:向服务器传输文件 / RESTful API中的更新数据。
DELETE:删除文件 / RESTful API中的删除数据。
HEAD:获得报文首部,类似GET但不返回报文主体部分,可用于获得URI的有效性及资源更新的日期时间等。
OPTIONS:获得对请求URI指定的资源支持的方法,如GET、POST、HEAD等。
TRACE:追踪路径,客户端使用该方法发送请求时在Max-Forwards首部字段中填入数值,每经过一个服务器该值会减1,若该值变为0则当前服务器返回状态码200响应。
CONNECT:请求用隧道协议连接代理,主要使用SSL和TLS将通信内容加密后经网络隧道传输,使用connect方法的格式为:CONNECT 代理服务器名:端口号 HTTP版本,如:

下面是使用浏览器访问http://192.168.70.5:8080页面中浏览器发送数据的抓包(浏览器中的请求是GET方法),前面为带参数name=leon,后面为不带参数,可以看到URL中的主机地址放到了Host首部中,路径和参数被存放在请求行中。

而对于POST模式,请求参数就是向服务端发送的实体数据,它们会被放到Body中。如下所示为html页面中的post请求与对应的http请求格式:


POST与GET的区别:POST的主要目的并不是像GET那样获取响应的主体内容,如下图所示,POST主要是向服务端发送实体主体的数据,而GET请求一般不会使用实体主体。也就是说POST主要用来向服务端推数据,GET用来从服务端拉数据。

由于post和get的使用差异才导致了二者的以下区别:
1)、get发送的数据有长度和类型限制,post则没有:因为发送数据时,POST是向实体主体body添加发送数据,GET方法是向请求的URL中添加发送数据(比如以参数形式),URL的长度是受限制的(最大2048个字符)。
2)、因为GET发送的数据是在URL中,所以GET只接受字符数据,POST则没有限制,在发送密码等敏感信息时不要使用GET,以免其在URL中直接展示。
3)、对于浏览器来说,它会因为POST的特性而对其在浏览器上的表现作出额外的限制:POST请求不会被缓存(除非手动设置);POST请求不会保留在浏览器历史记录中;POST请求不能被收藏为书签;
4)、还是对于浏览器来说,GET请求在三次握手之后直接发送GET请求头和数据(服务器返回200 OK响应),POST请求会在三次握手之后先发送post请求头,在服务器返回100 Continue响应后浏览器再发送数据,这之后服务器返回200 OK响应。因为POST会分两次传输,所以一般认为POST比GET要慢。但是在网上看到有人说并不是所有浏览器都会在POST的时候发送两次,Firefox就只发送一次。
三、http首部和响应状态码
1、http1.1首部字段
http首部字段由字段名+字段值构成,中间用分号“:”分隔,如果包含多个字段值的话用逗号“,”分隔,如:“Content-Type: text/html”,"Keep-Alive: timeout=15, max=100"。首部字段可以分为四种类型:
通用首部字段:

请求首部字段:

响应首部字段:

设置实体的首部字段:

如果按照缓存代理和非缓存代理的行为,可以将http首部字段分为端到端(End-to-end)首部和逐跳(Hop-by-hop)首部,逐跳首部只对单次转发有效,会因通过缓存或代理而不再转发。自http/1.1起,如果要使用hop-by-hop首部,需提供Connection首部字段,http/1.1中的逐跳首部字段有以下8个,除这8个首部字段外其它首部字段都属于端到端首部:

关于http首部字段的详细说明参考<<图解http>>。
2、http响应状态码

1xx :接收的请求正在处理。
200 OK:请求正常处理。
204 No Content:请求处理成功,且相应无资源返回。
206 Partial Content:请求处理成功,且该请求是范围请求。
301 Moved Permanently:永久性重定向即页面永久性移走,浏览器收到后会自动跳转到一个新的URL地址,且应该将保存的旧地址替换为新地址。
302 Found:页面临时性重定向,浏览器收到后会自动跳转到一个新的URL地址。
303 See Other:类似302功能,但它明确表示客户端应该使用GET方法重定向到指定资源。301、302标准是禁止将POST方法改变成GET方法的,但实际上几乎所有的浏览器都对301、302响应做出把POST改成GET后再次发送请求的做法。
304 Not Modified:资源已找到,但附带条件未满足。附带条件指采用GET方法的请求报文中If-Match、If-None-Match、If-Modified-Since等。
307 Temporary Redirect:类似302功能,但它明确表示客户端不应使用GET方法重定向到指定资源。
400 Bad Request:请求报文中存在语法错误,浏览器会像200 OK一样对待该状态码。
401 Unauthorized:发送的请求需要有http认证(BASIC认证、DIGEST认证)的信息,若之前已进行过一次请求,则表示用户认证失败。浏览器初次接收到401响应会弹出认证用的对话框。
403 Forbidden:访问被拒绝。
404 Not Found:资源未找到。
500 Internal Server Error:服务器执行出错。
502 Bad Gateway 网关错误。
503 Service Unavailable:服务器超负载或停机维护。如果知道服务器恢复的时间最后写入RetryAfter首部字段再返回给客户端。
504 Gateway Timeout网关超时
四、HTTPS
如下为HTTPS对比HTTP:

1、数据加密
现代加密方法中一般加密算法是公开的,但解密用的秘钥是保密的,比如发送方使用加密算法和秘钥对数据进行加密,接收方使用秘钥和解密算法对数据进行解密,这种发送方和接收方使用同一个秘钥的方式称为对称秘钥加密(共享秘钥加密)。对称加密的关键是怎样保证将秘钥安全的发送给对方,这可以通过非对称加密来实现。
非对称秘钥加密(公开秘钥加密)使用两把秘钥,一把私有秘钥只有自己知道,一把公开秘钥用来发送给对方:甲方生成私钥和公钥后向乙方发送公钥,乙方使用公钥对数据进行加密后将其发送给甲方,甲方再根据私钥对数据进行解密,乙方发送给甲方的数据或者甲方发送给乙方的公钥即使被第三方截取到也不能对数据进行解密,因为解密需要私钥。
对于HTTPS来说的话就是:HTTPS正式通信前,服务器会生成公钥和私钥,然后将公钥发送给客户端,客户端生成密钥后用服务器的公钥加密传回服务器,服务器用私钥解密得到密钥。然后客户端和服务端以后的通信就会使用这个秘钥来加密和解密数据,也就是先使用非对称加密生成秘钥,有了秘钥后就使用对称加密来通信。HTTPS中的这种加密手段是通过SSL协议完成的,TSL是以SSL为原型开发的协议,目前主流使用的版本是SSL3.0和TLS1.0(SSL1.0和2.0被发现存在问题,TLS目前还有1.1和1.2版本)。
2、身份认证
HTTPS通过证书机制,确保用户连接的是真正的服务器,而不是黑客模拟的服务器。如下所示,本来客户端A是要跟服务端B进行HTTPS通信的,但是A的请求被中间的黑客截获或者其伪装成了B,A在请求公钥的时候黑客会向其发送伪造的公钥来冒充B,对于B来说黑客会向其冒充A,这样使A、B之间通信的数据被黑客获取。

所以SSL握手中如何确保客户端收到的公钥就是来自确信的服务端呢?可以使用数字证书认证机构(Certificate Authority,简称CA)颁发的证书来解决该问题。服务端向CA发送公钥、域名等来申请认证(实际操作中,往往需要提供私钥,它会自动从私钥中提取公钥),CA会将服务端的公钥、域名、有效时间等信息放到一个证书中,CA还会对这些明文信息进行哈希计算得到一个哈希值,然后用CA的私钥对这个哈希值进行加密(这里被称为对证书进行签名,加密值即为签名值),然后将该签名值放到证书中一同颁发给服务端。客户端向服务端发出请求的话,服务端会先返回证书,客户端会对该证书中的明文信息进行哈希计算得到一个哈希值h1,然后客户端通过CA的公钥(大多数浏览器会内置常用认证机构的公钥)来对证书的签名值进行解密,解密得到的值如果与h1相同的话则证明该公钥是没有被篡改过的,服务端是可信赖的。
证书的另一个作用是可确认对方服务器背后的运营企业是否真实存在,一般这种网站会以绿色背景显示其域名,或有一个小锁的图标,点击它会提示当前连接是安全的,还可以查看证书的组织名和颁发机构等信息。使用由非知名的CA颁发的证书或者用OpenSSL自构建的证书(自签证书,即用自己的私钥签署的证书),浏览器会弹出以下的提示,因为浏览器没有内置该机构的公钥,无法对服务端发来的公钥进行验证,一般在非生产或非公开服务中可以使用自签证书。


4、将http网站升级为https
1)、获取SSL证书
可以通过JoySSL、Let's Encrypt或者阿里云、腾讯云来获得免费的证书,免费证书一般都会有有效期,到期后需要续签(JoySSL支持自动续签)。使用JoySSL的话,进入其官网,注册账号并填写注册码(230919或230923),选择证书类型(单域名、通配符等,单域名只对应一个域名,多域名可以供多个域名使用,通配符则支持多个同级子域名,如bbs.wosign.com和freessl.wosign.com),提交域名验证信息,然后下载证书文件(包含证书、私钥)。可以使用自动化工具Certbot来获得Let's Encrypt证书,并且可以配置自动续签。证书可以分为dv、ov、ev等,dv适合个人和小型网站(无法显示企业信息),ov适合企业网站,ev适合金融等安全要求高的企业。申请阿里云免费证书的话,可以通过“宝塔”服务器管理面板来进行。
2)、配置服务器启用证书
不同服务器上指定证书的方法不同,Apache的话修改httpd.conf或ssl.conf指定证书路径,Nginx的话在配置文件中添加SSL监听端口443并关联证书, 如下所示:

HTTPS重定向:在服务器配置中添加301重定向规则,将HTTP请求自动跳转到HTTPS,如下为Nginx中的配置:

确保图片、CSS、JS等资源使用https以避免混合内容警告。
3)、验证和优化
使用SSL Labs检测网站证书配置评分。
在Google Search Console和Google Analytics中提交http站点地图,将网站更新为https版本。
5、SpringBoot中开发HTTPS接口
1)、生成SSL证书
如果是开发环境的话,可以使用JDK的keytool生成自签名证书,如下所示,执行命令后按提示输入姓名、密码等证书信息。也可以从权威 CA(如阿里云、Let's Encrypt)申请免费证书,审核通过后下载 .jks 或 .p12 文件,然后将生成的证书如keystore.p12放到src/main/resources目录。

2)、配置HTTPS
在application.yml或application.properties中配置如下,其中生产环境建议使用 server.port=443,若证书不在resources目录,证书路径需要指定绝对路径,如file:/path/to/keystore.p12。

3)、重定向HTTPS
如果接口已经存在并且是http的话,需要进行重定向,可以通过配置类实现,如下所示为针对tomcat容器的配置,此配置会将所有 HTTP 请求(如 http://domain:8080)重定向到 HTTPS(如 https://domain:8443):

4)、验证及注意事项
使用浏览器(使用自签名证书的话会出现安全警告)或curl命令(curl -l https://localhost:8443/my-api)测试接口。
避免在代码中硬编码证书密码,可以通过环境变量注入,如key-store-password:${SSL_PASSWORD}。
证书格式推荐使用PKCS12, JKS已逐渐淘汰。
6、总结
可以说HTTPS = TLS/SSL + HTTP,如下所示,它通过加密解决了攻击者窃听和篡改请求或响应的数据,通过使用证书解决了无法验证通信方身份的问题。

HTTPS会将整个HTTP报文(开始行、头部、实体)进行加密,但是TLS握手中的一些信息不会进行加密,如客户端发送的Client Hello消息和服务器返回的Server Hello消息,而Client Hello消息中有个 server name 字段,它就是请求的域名地址,所以可以在抓包的时候看到请求的域名,如下所示。

HTTPS相对HTTP多出了SSL通信(七次握手)以及加密解密运算,所以其可能会比HTTP要慢2到100倍,可以使用SSL加速器这种(专用服务器)硬件来改善该问题。HTTPS通信中的SSL握手通信具体步骤可以在《图解HTTP》书中查看。
HTTPS默认端口号为443,HTTP协议代理服务器常用端口号:80/ 8080/ 3128/ 8081/ 9098。
使用Postman或Apipost进行https通信的话,可以在Settings设置中设置不启用SSL 证书验证(默认)来忽略对服务端证书的验证。
浙公网安备 33010602011771号