HTTP实用指南 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的第5天
前置知识:队头阻塞问题
对头阻塞是性能受限的现象,由于一列的第一个数据包(对头)受阻导致整列数据包受阻。TCP是面向流的,向上层提供的数据是有序的,当发生丢包或乱序时,TCP协议会通过重传等待数据满足有序后再提交给应用层,以此造成队头阻塞,只要在传输层采用TCP协议都会面临该问题
协议发展
HTTP/0.9
- 只有get请求方法
- 单行协议,响应只有HTML文档
- 发送的请求没有版本号且无请求头
HTTP/1.0
- 扩展了支持的请求方法,包括GET,POST,HEAD.
- 新增5类状态码,响应信息(1xx), 成功响应(2xx), 重定向响应(3xx), 客户端错误响应(4xx)和服务端响应(5xx)
- 定义了消息头部,由键值对构成 缺陷
- 默认不提供keepalive, 需要显示设置
- 不支持连接复用,每次请求都需要TCP三次握手
- 请求blocking,即下一个请求的发送必须在收到前一个请求包的响应后 造成请求延迟大,网络带块资源无法被充分利用
HTTP/1.1
- 缓存机制(强缓存和协商缓存)
- 扩展错误状态码
- 新增Host字段,可以将请求发往同一台服务器的不同网站
- 默认开启keepalive,可以被多个请求复用,减少频繁断开连接,建立连接的开销;可以通过设置connection:close关闭
- 请求范围引入range域,支持获取文件的部分内容,为并行下载和断点续传提供了技术支持
- 引入管道机制,允许客户端同时发送多个请求,一定程度缓解队头阻塞(服务器仍必须按照客户端请求先后顺序依次响应结果)
HTTP/2.0
http2.0主要基于spdy协议的http协议的增强,目标是优化http协议的性能,通过压缩,多路复用,服务器推送,优先级等技术缩短网页加载时间并提高安全性。
- 二进制分帧:在网络层和传输层之间增加一个
二进制分帧层,将传输的数据分成更小的信息和帧通过二进制编码压缩传送(首部信息封装到header帧,request body封装到Data帧) - 首部压缩:使用
Hpack编码对首部进行压缩,减少header的大小,并在两端维护索引表,用于记录出现的header信息,在传输过程中可以通过传输已经记录过的header键名,通过索引键名获取header信息。 - 多路复用:http1.+版本中当出现页面需要请求很多资源的时候容易导致队头阻塞,资源需要等待其他资源请求完成后才能继续发送;基于二进制分帧传输的http2.0可以在共享tcp连接的基础上同时发送请求和响应,将http信息分解成独立的帧交错发送出去,在另一端通过标识符和首部重新进行组装,极大的提高了传输性能。
HTTP队头不会阻塞,但仍然存在传输层方面的队头阻塞,在传输层协议采用TCP - 请求优先级:将信息分为许多独立帧后可以优化帧的交错和传输顺序进一步优化性能
- 服务器推送:服务器可以在客户端没有明确的请求时发送多个响应,推送额外的资源给客户端(比如请求html资源时会将相关的js和css一同发送)
HTTP/3.0
使用UDP作为传输层协议,因为UDP不提供可靠和流控服务,需要另外的协议QUIC配合,QUIC工作在应用层,位于UDP和HTTP之间,可以保证数据有序,拥塞控制等,还集成了TLS 解决的问题
- 由于绕开了TCP,解决了因为TCP导致的队头阻塞问题
- 不在需要TCP三次握手,降低了请求延迟
- 解决了连接迁移问题,使用TCP作传输层协议时,移动网络切换WIFI涉及到IP改变,需要重新建立连接,HTTP3使用CID(connection id)来标识一条流
| HTTP0.9 | HTTP1.0 | HTTP1.1 | HTTP2.0 | HTTP3.0 | |
|---|---|---|---|---|---|
| 消息文本 | ASCII | ASCII | ASCII | 二进制 | 二进制 |
| TCP队头阻塞 | YES | YES | YES | YES | NO |
| HTTP队头阻塞 | YES | YES | YES | NO | NO |
| 首部压缩 | NO | NO | NO | YES | YES |
协议报文相关
请求方法
| Method | |
|---|---|
| get | 请求指定的页面信息并返回实体主体 |
| Head | 类似于get请求但返回的响应中没有具体的内容获取报头 |
| Post | 向指定资源提交数据进行处理请求(提交表单或者上传文件)。数据被包含在请求头中,post请求可能会导致新的资源的建立或已有资源的修改 |
| Delete | 请求服务器删除指定的页面 |
| Connect | http1.1协议中预留给能够将连接改为管道方式的代理服务器 |
| Trace | 回显服务器收到的请求,主要用于测试或诊断 |
| OPTIONS | 描述目标资源的通信选项,在CORS的预检请求中能看到 |
| Patch | put方法的补充,对已知资源进行局部更新 |
请求头
| 请求头 | |
|---|---|
| Accept | 接收类型,表示浏览器支持的MIME类型(对标服务端返回的Content-Type) |
| Cache-Control | 指定请求和响应遵循的缓存机制,如no-cache,no-store |
| If-Modified-Since | 对应服务端的Last-Modified,用来匹配文件是否变动,精确到1s |
| Expires | 缓存控制,在该时间内不会请求,直接使用缓存,服务端时间 |
| Max-age | 代表资源在本地缓存多少秒,有效时间内不会请求,而是使用缓存 |
| If-None-Match | 对应服务端的ETag,匹配文件内容是否发生改变(非常精确) |
| Cookie | 有Cookie并且同域访问时会携带 |
| Refer | 该页面的来源URL(会精确到详细页面地址,csrf拦截常用该字段)· |
| Origin | 最初的请求从哪里发起(精确到端口),相比Refer更尊重隐私 |
| User-Agent | 用户客户端必要信息,UA头部等 |
| Content-Type | 服务器返回的实体内容的类型 |
| Set-Cookie | 设置和页面关联的cookie,服务器通过该头部把cookie发送给客户端 |
| Server | 服务器的一些相关信息 |
| Access-Control-Allow-Origin | 服务器允许请求Origin头部 |
Origin和Refer的区别:origin将URI的请求参数剥离,Refer保留
Cookie内容
| Set-Cookie -response | ||
|---|---|---|
| Name=value | 各种cookie的名称和值 | |
| Expires=Date | Cookie的有效期,缺省时仅在浏览器关闭之前有效 | |
| Path=Path | 限制指定Cookie的发送范围的文件目录,默认为当前 | |
| Domain=domain | 限制cookie生效的域名 | 默认为创建cookie的服务域名 |
| Secure | 仅在HTTPS安全连接时才可以发送Cookie | |
| HttpOnly | JS脚本无法获得Cookie | |
| SameSite=[None|Strinct|Lax] | None,同站跨站都可发送 Strict,仅在统战发送 Lax,允许与顶级导航一起发送,并将第三方网站发起的GET请求一起发送 |
强缓存和协商缓存
浏览器缓存就是把一个已经请求过的Web资源拷贝一份副本储存在浏览器中,当下一个请求来到时若相同的URL,缓存会根据缓存机制决定是否直接使用副本响应访问请求还是向源服务器再次发送请求
缓存机制:
- 强缓存优先于协商缓存进行,若强制缓存(Expires[毫秒]和Cache-Control[秒],cache-control的优先级更高)生效则直接使用缓存,通过访问本地缓存直接验证查看是否过期,不生效则进行协商缓存(Last-Modified/If-Modified-Since和Etag/If-None-Match)
- 通过访问本地缓存查看是否有Etag字段内容,有则向服务器发送请求,请求的内容中含有If-None-Match字段,内容为Etag中的内容,服务器会根据该资源当前的内容生成对应的标识字符串与该字段进行对比,一致则返回304从本地缓存获取,否则返回新的资源(状态码200)并修改返回的etag字段为新的值,Last-Modified是对比时间
SSO单点登录
在多个系统或打开的多个页面中,只需要某个系统或页面中登陆一次,就可以访问其他相互信任的应用系统
普通登录认证流程
- 在一次登录完成后,会在该用户的session中标记登录状态为yes,同时liu/lanqi中写入Cookie,该Cookie是这个用户的唯一标识
- 在下一次访问该应用时,请求会携带Cookie,服务端根据Cookie找到对应的Session
- 通过Session判断该用户的登录状态,若不做特殊配置,该Cookie也叫Session ID,在服务端是唯一的
同域下的单点登录
在普通登录认证机制中得知,在sso中写入了cookie,如何才能在b端和a端也能登录呢? 出现的问题:
- Cookie是不能跨域的,在b端和a端发送请求时不能携带sso的cookie
- sso,a端和b端时不同的应用,它们的session存在自己的应用中,不被共享 解决方案
- sso登录以后将Cookie设置为顶域,这样所有子域系统都可以访问到顶域的Cookie。注意在设置Cookie时,只能设置顶域和自己的域,不能设置其他域
- 将3个系统的session进行共享,可以使用Spring-Session等方案
这样同域名下的单点登录就实现了
不同域下的单点登录
同域下的单点登录是巧妙利用了Cookie顶域的特性,不同域的实现流程如下
- 用户访问a系统,a系统需要登录,但此时并未登录
- 点击登录跳转到CAS server即SSO登录系统,SSO系统也未登录,弹出用户登录页
- 用户填写用户名,密码,SSO系统进行认证后,将登录状态写入SSO的session,浏览器中写入SSO域下的Cookie
- SSO系统登录完成会生成ST(Service Ticket),然后跳转到a系统,同时将ST作为参数传递给a系统
- a系统拿到ST后,从后台向SSO发送请求,验证ST是否有效
- 验证通过后,a系统将登录状态写入session并设置a域下的Cookie 至此,跨域单点登录完成,访问a2系统时的流程如下:
- 用户访问a2系统,a2系统没有登录,跳转到SSO。
- 由于SSO已经登录了,不需要重新登录认证。
- SSO生成ST,浏览器跳转到a2系统,并将ST作为参数传递给a2。
- a2拿到ST,后台访问SSO,验证ST是否有效。
- 验证成功后,a2将登录状态写入session,并在a2域下写入Cookie。 a2系统不需要走登录流程,就已经是登录了。SSO,a和a2在不同的域,它们之间的session不共享也是没问题的
实战demo待更新...