HTTP - HTTP报文首部
HTTP请求报文与响应报文格式
请求报文包含四部分:
a、请求行:包含请求方法、URI、HTTP版本信息 b、请求首部字段 c、请求内容实体 d、空行
响应报文包含四部分:
a、状态行:包含HTTP版本、状态码、状态码的原因短语 b、响应首部字段 c、响应内容实体 d、空行
一. 请求报文
image二. 响应报文
image三. HTTP1.1下4种首部字段
1. 请求首部字段
字段名 | 描述 |
---|---|
Accept | 用户代理可处理的媒体类型 |
Accept-Charset | 优先的字符集 |
Accept-Encoding | 优先的内容编码 |
Accept-Language | 优先的语言 |
Authorization | Web认证信息 |
Proxy-Authorization | 代理服务器要求的认证信息 |
Expect | 期待服务器的特定行为 |
From | 用户的电子邮箱地址 |
Host | 请求资源所在服务器 |
Referer | 对请求中 URI 的原始获取方 |
If-Match | 比较实体标记(ETag) |
If-None-Match | 与 If-Match 相反 |
If-Modified-Since | 比较资源的更新时间 |
If-Unmodified-Since | 与If-Modified-Since相反 |
If-Range | 资源未更新时发送实体 Byte 的范围请求 |
Range | 实体的字节范围请求 |
Max-Forwards | 最大传输逐跳数 |
TE | 传输编码的优先级 |
User-Agent | HTTP 客户端程序的信息 |
2. 响应首部字段
字段名 | 描述 |
---|---|
Accept-Ranges | 是否接受字节范围请求 |
Age | 推算资源创建经过时间 |
ETag | 资源标志 |
Location | 令客户端重定向至指定URI |
Proxy-Authenticate | 代理服务器对客户端的认证信息 |
Retry-After | 对再次发起请求的时机要求 |
Server | HTTP服务器的安装信息 |
Vary | 代理服务器缓存的管理信息 |
WWW-Authenticate | 服务器对客户端的认证信息 |
3. 通用首部字段
字段名 | 描述 |
---|---|
Cache-Control | 控制缓存 |
Connection | 逐跳首部、连接的管理 |
Date | 创建报文的日期时间 |
Pragma | 报文指令 |
Trailer | 报文末端的首部一览 |
Transfer-Encoding | 指定报文主体的传输编码方式 |
Upgrade | 升级为其他协议 |
Via | 代理服务器的相关信息 |
Warning | 错误通知 |
4. 实体首部字段
字段名 | 描述 |
---|---|
Allow | 支持的HTTP方法 |
Content-Encoding | 实体主体适用的编码方式 |
Content-Language | 实体主体的自然语言 |
Content-Length | 实体主体的大小(单位:字节) |
Content-Location | 替代对应资源的URI |
Content-MD5 | 实体主体的报文摘要 |
Content-Range | 实体主体的位置范围 |
Content-Type | 实体主体的媒体类型 |
Expires | 资源有效时间 |
Last-Modified | 资源的最后修改日期时间 |
5. 其他字段(非HTTP1.1定义)
字段名 | 描述 |
---|---|
Cookie | 请求首部 |
Set-Cookie | 响应首部 |
Access-Control-Allow-Origin | 响应首部 |
Access-Control-Allow-Headers | 响应首部 |
Access-Control-Allow-Methods | 响应首部 |
一、HTTP协议的主要特点
- 简单快速:每个资源URI都是固定的
- 灵活:头部有数据类型,可以完成不同类型传输
- 无连接:传输完成即断开
- 无状态:建立连接 完成传输 下一次客户端传输 是不知道两次连接者的身份的
- 应用层协议
其他协议:SOAP是一种简单基于XML的轻量协议
二、HTTP报文的组成部分
- 请求报文
- 请求行(首行):HTTP方法、页面地址(/表示首页)、HTTP协议/版本
- 请求头:HTTP协议告诉服务端要哪些内容(key/value值)
- 空行:告诉服务端,请求头部分结束
- 请求体
- 响应报文
- 状态行:HTTP协议、状态码
- 响应头
- 空行
- 响应体
2.1 浏览器输入一个url后关于HTTP请求发生了什么
image2.3 数据协商 - HTTP请求头
Header | 解释 | 示例 |
---|---|---|
Accept | 指定客户端能够接收的内容类型 | Accept: text/plain, text/html |
Accept-Charset | 浏览器可以接受的字符编码集。 | Accept-Charset: iso-8859-5 |
Accept-Encoding | 指定浏览器可以支持的web服务器返回数据压缩编码类型。 | Accept-Encoding: compress, gzip |
Accept-Language | 浏览器可接受的语言 | Accept-Language: en,zh |
Accept-Ranges | 可以请求网页实体的一个或者多个子范围字段 | Accept-Ranges: bytes |
Authorization | HTTP授权的授权证书 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Cache-Control | 指定请求和响应遵循的缓存机制 | Cache-Control: no-cache |
Connection | 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) | Connection: keep-alive |
Cookie | HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 | Cookie: $Version=1; Skin=new; |
Content-Length | 请求的内容长度 | Content-Length: 348 |
Content-Type | 请求的与实体对应的MIME信息 | Content-Type: application/x-www-form-urlencoded |
Date | 请求发送的日期和时间 | Date: Tue, 15 Nov 2010 08:12:31 GMT |
Expect | 请求的特定的服务器行为 | Expect: 100-continue |
From | 发出请求的用户的Email | From: user@email.com |
Host | 指定请求的服务器的域名和端口号 | Host: www.baidu.com |
If-Match | 只有请求内容与实体相匹配才有效 | If-Match: “737060cd8c284d8af7ad3082f209582d” |
If-Modified-Since | 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 | If-Modified-Since: Wed, 31 Oct 2018 05:10:43 GMT |
If-None-Match | 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 | If-None-Match: “737060cd8c284d8af7ad3082f209582d” |
If-Range | 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag | If-Range: “737060cd8c284d8af7ad3082f209582d” |
If-Unmodified-Since | 只在实体在指定时间之后未被修改才请求成功 | If-Unmodified-Since: Wed, 31 Oct 2018 05:10:43 GMT |
Max-Forwards | 限制信息通过代理和网关传送的时间 | Max-Forwards: 10 |
Pragma | 用来包含实现特定的指令 | Pragma: no-cache |
Proxy-Authorization | 连接到代理的授权证书 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
Range | 只请求实体的一部分,指定范围 | Range: bytes=500-999 |
Referer | 先前网页的地址,当前请求网页紧随其后,即来路 | Referer: https://www.baidu.com/ |
TE | 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 | TE: trailers,deflate;q=0.5 |
Upgrade | 向服务器指定某种传输协议以便服务器进行转换(如果支持) | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
User-Agent | User-Agent的内容包含发出请求的用户信息(判断返回PC端的页面还是移动端的页面) | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 |
Via | 通知中间网关或代理服务器地址,通信协议 | Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) |
Warning | 关于消息实体的警告信息 | Warn: 199 Miscellaneous warning |
2.4 数据协商 - HTTP响应头
Header | 解释 | 示例 |
---|---|---|
Accept-Ranges | 表明服务器是否支持指定范围请求及哪种类型的分段请求 | Accept-Ranges: bytes |
Age | 从原始服务器到代理缓存形成的估算时间(以秒计,非负) | Age: 12 |
Allow | 对某网络资源的有效的请求行为,不允许则返回405 | Allow: GET, HEAD |
Cache-Control | 告诉所有的缓存机制是否可以缓存及哪种类型 | Cache-Control: private |
Content-Encoding | web服务器支持的返回内容压缩编码类型。 | Content-Encoding: gzip |
Content-Language | 响应体的语言 | Content-Language: en,zh |
Content-Length | 响应体的长度 | Content-Length: 348 |
Content-MD5 | 返回资源的MD5校验值 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
Content-Range | 在整个返回体中本部分的字节位置 | Content-Range: bytes 21010-47021/47022 |
Content-Type | 返回内容的MIME类型 | Content-Type: application/x-www-form-urlencoded; charset=utf-8 |
Date | 原始服务器消息发出的时间 | Date: Wed, 31 Oct 2018 05:10:43 GMT |
etag | 请求变量的实体标签的当前值 | etag: W/"847a3c5130b6c83cd331dee376e0d0a3" |
Expires | 响应过期的日期和时间 | Expires: Wed, 31 Oct 2018 05:10:43 GMT |
Last-Modified | 请求资源的最后修改时间 | Last-Modified: Wed, 31 Oct 2018 05:10:43 GMT |
Location | 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源(301/302) | Location: https://www.hao123.com/ |
Pragma | 包括实现特定的指令,它可应用到响应链上的任何接收方 | Pragma: no-cache |
Proxy-Authenticate | 它指出认证方案和可应用到代理的该URL上的参数 | Proxy-Authenticate: Basic |
refresh | 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持) | Refresh: 5; url=https://www.hao123.com/ |
Retry-After | 如果实体暂时不可取,通知客户端在指定时间之后再次尝试 | Retry-After: 120 |
Server | web服务器软件名称 | Server: BWS/1.0 |
Set-Cookie | 设置Http Cookie | Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1 |
Trailer | 指出头域在分块传输编码的尾部存在 | Trailer: Max-Forwards |
Transfer-Encoding | 文件传输编码 | Transfer-Encoding:chunked |
Vary | 告诉下游代理是使用缓存响应还是从原始服务器请求 | Vary: * |
Via | 告知代理客户端响应是通过哪里发送的 | Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) |
Warning | 警告实体可能存在的问题 | Warning: 199 Miscellaneous warning |
WWW-Authenticate | 表明客户端请求实体应该使用的授权方案 | WWW-Authenticate: Basic |
三、HTTP协议的发展历史
HTTP/0.9:
- 只有一个命令GET
- 没有 header 等描述数据的信息
- 服务器发送完毕,就关闭TCP连接
HTTP/1.0:
- 增加了很多命令
- 增加了status code 和 header
- 多字符集支持、多部分发送、权限和缓存等
HTTP/1.1:
- 持久连接
- pipeline
- 增加 host (可以在一个集群上同时跑多个web服务,通过host字段来判断使用node还是Java服务,提高物理服务的使用效率)和其他一些命令
HTTP/2.0:
- 分帧传输:所有数据以二进制(帧)传输,之前都是用字符串
- 多路复用(信道复用):同一个连接中发送多个请求,不再需要按照顺序来
- 头信息压缩(之前都是完整发送和返回,占用带宽的量比较大)以及推送(支持server push,即服务端可以主动发送数据传输)等提高效率的功能
场景:web页面有html、css等文件,有根据请求的url才能解析出html等文件的路径。这里就会包含执行顺序的问题,使用HTTP/2.0之前的协议首先要先请求、解析之后才能获取html、css、js,而且浏览器的并发请求数目是一定的,即对同一域名下的请求有一定数量限制(一般为6-10个),超过限制数目的请求会被阻塞。而HTTP/2.0 中只创建一个HTTP连接,数据传输和请求的发送是并行的,会改善效率、减少握手开销。
四、TCP 三次握手和四次挥手
4.1 三次握手
类比:
客户:在吗?我想跟你聊天。(发送SYN请求同步报文)
服务:好的,我听着呢。(发送SYN请求同步报文,确认同步)你说吧。(发送ACK确认报文,即可以开始吐槽了)
客户:好的。(发送ACK确认报文)今天...(开始吐槽)
图例:
-
第一次握手(SYN=1, seq=J):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 J,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入
SYN_SEND
状态。 -
第二次握手(SYN=1, ACK=1, seq=K, ACKnum=J+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即J+1。 发送完毕后,服务器端进入
SYN_RCVD
状态。 -
第三次握手(ACK=1,ACKnum=K+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
发送完毕后,客户端进入
ESTABLISHED
状态,当服务器端接收到这个包时,也进入ESTABLISHED
状态,TCP 握手结束。
为什么要三次握手:防止无用连接,规避网络延迟等原因造成的网络开销浪费的问题。
4.2 四次挥手
类比:
客户:我有事儿要挂电话了。(发送FIN结束报文,1次挥手)
服务:好吧(发送ACK确认报文,2次挥手),对了,还有个事儿要跟你说。
......
服务:说完了,挂了吧。(发送FIN结束报文,3次挥手)
客户:好的,拜拜。(发送ACK确认报文,4次挥手)
服务挂断电话.....
2MSL后......
客户:我知道了。
啪!(这才断开连接)
图例:
- 第一次挥手(FIN=1,seq=M)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。发送完毕后,客户端进入 FIN_WAIT_1
状态。
- 第二次挥手(ACK=1,ACKnum=M+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。发送完毕后,服务器端进入 CLOSE_WAIT
状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2
状态,等待服务器端关闭连接。
- 第三次挥手(FIN=1,seq=K)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。发送完毕后,服务器端进入 LAST_ACK
状态,等待来自客户端的最后一个ACK。
- 第四次挥手(ACK=1,ACKnum=K+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT
状态,等待可能出现的要求重传的 ACK 包。服务器端接收到这个确认包之后,关闭连接,进入 CLOSED
状态。客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED
状态。
五、TCP、UDP对比
TCP | UDP | |
---|---|---|
可靠性 | 可靠 | 不可靠 |
连接性 | 面向连接 | 无连接 |
报文 | 面向字节流 | 面向报文 |
效率 | 传输效率低 | 传输效率高 |
双工性 | 全双工 | 一对一、一对多、多对一、多对多 |
流量控制 | 有(滑动窗口) | 无 |
拥塞控制 | 有(慢开始、拥塞避免、快重传、快恢复) | 无 |
六、HTTP方法
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
HTTP方法名 | 作用 |
---|---|
GET | 获取资源 |
POST | 传输资源 |
PUT | 更新/修改资源 |
DELETE | 删除资源 |
HEAD | 获得报文首部 |
CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 |
OPTIONS | 返回服务器针对特定资源所支持的HTTP请求方法,允许客户端发送'*' 查看服务器的性能 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
推荐:同一个url通过不同的方法来实现,设计符合RESTful风格的接口。
七、POST和GET区别
场景 | GET | POST |
---|---|---|
浏览器回退 | 无害的 | 会重复提交请求 |
产生的URL地址 | 可以被收藏 | 不可以 |
浏览器缓存 | 主动缓存 | 不会自动缓存,除非手动设置 |
编码方式 | 只能进行URL编码 | 支持多种编码方式 |
浏览器历史记录中的请求参数 | 完整保留 | 不会保留 |
URL中传递的参数长度 | 2KB左右,不同浏览器限制不同。故参数不要太长,容易被浏览器截断。 | 没有限制 |
参数的数据类型 | ASCII字符 | 没有限制 |
安全性 | 参数直接暴露在URL上,还可能造成CSRF攻击(以?分割URL和传输数据,多个参数用&连接) | 可以传递敏感信息 |
参数传递 | URL传递 | Request body中 |
八、HTTP CODE
一个好的HTTP服务可以通过CODE判断结果
1XX:指示信息 - 表示请求已接收,继续处理
2XX:成功 - 表示请求已被成功接收
- 200 OK:客户端请求成功
- 201 Created:请求成功并且服务器创建了新的资源
- 202 Accepted:服务器已接受请求,但尚未处理
- 206 Partial Content:客户端发送了一个带有Range头的GET请求,服务器完成了它
场景:video播放视频地址/audio播放音频地址,如果文件过大,则一般会返回206
3XX:重定向 - 要完成请求必须进行更进一步的操作
- 301 Moved Permanently:所请求的页面已经永久转移至新的URL(永久重定向)
- 302 Found:所请求的页面已经临时转移至新的URL(临时重定向)
- 303 See Other:临时性重定向,且总是使用 GET 请求新的 URI
- 304 Not Modified:客户端有缓冲的文档并发出一个条件性的请求,服务器告诉客户原来缓存的文档还可以继续使用
- 307 Temporary Redirect:临时性重定向,除GET、HEAD方法外,其他的请求方法必须等客户确认才能跳转
4XX:客户端错误 - 请求有语法错误或请求无法实现
- 400 Bad Request:客户端有语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权,这个状态码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden:对被请求页面的访问被禁止
- 404 Not Found:请求资源不存在
5XX:服务器错误 - 服务器未能实现合法的请求
- 500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用。一般来说,这个问题都会在服务器端的源代码出现错误时出现
- 502 Bad Gateway:从上游服务器接收到无效的响应
- 503 Server Unavailable:请求未完成,服务器临时过载或宕机(可能是过载或正在维护),一段时间后可能恢复正常。看Retry-After头,可以预计延迟时间,如果没给出Retry-After头,那么客户端应当以处理500响应的方式处理它
- 504 Gateway Timeout:网页请求超时
场景:访问大流量或者内容数据量较多的网站。根据我们掌握的服务器性能状况及网络流量情况,合理的对nginx.conf中的字句进行合理正确的设置。
九、持久连接 - TCP connection
HTTP1.0:采用“请求-应答”模式,每个请求/应答客户和服务器都要新建一个连接,完成后立即断开连接。
HTTP1.1:
- 当使用普通模式,即非Keep-Alive模式时,同HTTP1.0.
- 当使用
Keep-Alive
模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或重新建立连接(减少三次握手的开销)。
HTTP2.0:支持信道复用,即HTTP请求支持并发。同一个用户对同一个服务器发起网页请求的时候(同域),只需要一个HTTP连接。
十、管线化
在使用持久连接的情况下,某个连接上消息的传递类似于:
请求1 -> 响应1-> 请求2 -> 响应2 - > 请求3 -> 响应3
管线化之后,某个连接上的消息变成了类似这样:
请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3
管线化的特点
- 管线化机制通过持久连接完成,仅HTTP/1.1支持此技术
- 只有GET和HEAD请求可以进行管线化,而POST则有所限制
- 初次创建连接时不建议启动管线机制,因为服务器不一定支持HTTP/1.1版本的协议
- 管线化不会影响响应到来的顺序,响应返回的顺序并未改变
- HTTP/1.1要求服务端必须支持管线化,但并不是要求服务器也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
- 由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因此现代浏览器如Chrome和Firefox默认并未开启管线化支持。
十一、创建一个web服务(基于node.js)
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
response.end('OK')
}).listen(8888)
console.log('server is listening... ')
十二、跨域通信
浏览器有同源策略。协议、域名、端口有一个不同就算跨域。跨域请求,虽然会成功,但是由于同源安全策略限制不会返回相应的资源。
主要限制以下几个方面:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 无法获得
- AJAX 请求不能发送
同源策略:限制从一个源加载的文档或脚本和来自另一个源的资源进行交互。这是用于隔离潜在的恶意文件的关键的安全机制。
前后端如何通信:
- AJAX
- WebSocket
- CORS
...
跨域通信几种解决方案
- JSONP
- CORS(可以理解成支持跨域通信的AJAX)
- Hash
- window.name/window.postMessage(HTML5)
- WebSocket
- Proxy
12.1 JSONP
JSONP:由于同源策略的限制,XMLHttpRequest只允许请求当前源的资源。而<script src="...">...</script>
,<img>
,<link>
标签没有同源限制,所以JSONP通过声明动态<script>
标签,实现异步加载,为src指定一个跨域url。绕过同源策略的限制,拿到数据。
JSONP由两部分组成:回调函数和数据。
回调函数:当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据(注意圆括号和大括号的书写)。利用回调函数处理JSON数据。
优点:
- 兼容性好
缺点:
- 只支持GET请求,不支持 POST 和其他请求
- 只支持跨域HTTP请求,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
简述原理与过程:首先在客户端注册一个 callback, 然后把callback的名字传给服务器。此时,服务器先生成一个function , function 名字就是传递上来的参数。最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 JS 语法的文档,返回给客户端。客户端浏览器解析script标签,并执行返回的 JS 文档,此时数据作为参数,传入客户端预先定义好的callback 函数里。
12.2 CORS
CORS:通过设置Access-Control-Allow-Origin:'*'
来允许跨域。
*
表示所有的服务都接受允许跨域,这里最好设置成特定的域名。如:'http://100.79.238.34:8888'
。
注:此属性只能设置一个值,但是可以动态判断服务允不允许跨域,允许就写进去。
优点:
- 可用Ajax发请求获取数据
缺点:
- 兼容性没有JSONP好(需要浏览器支持XHR2)
12.2.1 CORS 预请求
CORS 限制:
- 默认只支持GET、POST、HEAD请求
- 允许的Content-Type:
text/plain
、multipart/form-data
、application/x-www-form-urlencoded
- 请求头限制
- XMLHttpRequestUpload 均没有注册任何事件监听器
- 请求中没有使用 ReadableStream 对象
详细限制请看官方文档。
使用CORS预请求(preflight),突破跨域限制
CORS预请求:就是在发生CORS请求时,浏览器检测到跨域请求,会自动发出一个OPTIONS请求来检测本次请求是否被服务器接受。
一个OPTIONS请求一般会携带下面两个与CORS相关的头:
- Access-Control-Request-Method : 本次预检请求的请求方法。
- Access-Control-Request-Headers:本次请求所携带的自定义首部字段。
这些字段是导致产生OPTIONS请求的一个原因。
这样,服务端收到该预检请求后,会返回与CORS相关的响应头。主要会包括下面几个,但可能还会有其他的有关CORS字段:
- Access-Control-Allow-Origin: 服务器允许的跨域请求源
- Access-Control-Allow-Methods: 服务器允许的请求方法
- Access-Control-Allow-Headers: 服务器允许的自定义的请求首部字段
- Access-Control-Allow-Max-Age:指定本次预检请求的有效期,单位为秒
- Access-Control-Allow-Credentials:接收跨域的Cookie
12.3 跨域相关拓展:
- 通过
NODE
发起请求可以避免 浏览器 的同源策略。 - nginx 设置反向代理
server {
listen 80;
server_name www.a.com;
access_log logs/test.access.log;
# 匹配以/apis/开头的请求
location ^~ /apis/ {
proxy_pass http://www.b.com/; #注意域名后有一个/
}
location / {
root html/a;
index index.html index.htm;
}
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
还有Proxy、window.name、window.postMessage 等跨域方式,感兴趣可以查阅相关资料。
十三、缓存优化
13.1 协议层面
- 强缓存:强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。
强缓存主要是采用响应头中的
Cache-Control
和Expires
两个字段进行控制的。其中Expires是HTTP 1.0中定义的,它指定了一个绝对的过期时期。而Cache-Control是HTTP 1.1时出现的缓存控制字段。Cache-Control:max-age
定义了一个最大使用期,就是从第一次生成文档到缓存不再生效的合法生存日期。由于Expires是HTTP1.0时代的产物,因此设计之初就存在着一些缺陷,如果本地时间和服务器时间相差太大,就会导致缓存错乱。这两个字段同时使用的时候Cache-Control的优先级给更高一点。 这两个字段的效果是类似的,客户端都会通过对比本地时间和服务器生存时间来检测缓存是否可用。如果缓存没有超出它的生存时间内,客户端就会直接采用本地的缓存。如果生存日期已经过了,这个缓存也就宣告失效。接着客户端将再次与服务器进行通信来验证这个缓存是否需要更新。
- 协商缓存:当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些
http header
验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回(304
),若未命中请求,则将资源返回客户端,并更新本地缓存数据(200
)。
强缓存机制如果检测到缓存失效,就需要进行服务器再验证。这种缓存机制也称作协商缓存。浏览器在第一次获取请求的时候,就会在响应头中携带上资源的上次服务器修改日期(Last-Modified)或者资源的标签(Etag)。后续的请求服务器会根据请求头上的If-Modified-Since(对应Last-Modified)和(If-None-Match)字段来判断资源是否失效,一旦资源过期,则服务器会重新发送新的资源到客户端上,从而保证资源的有效性。
另外一种协商缓存的校验方式的通过校验码而不是时间,这样就保证了在文件内容不变的情况下不会重复占用网络资源。响应头中Etag字段是服务器给资源打上的一个标记,利用这个标记就可以实现缓存的更新。后续发起的请求,会在请求头上附带上If-None-Match字段,其值就是这个标记的值。
需要注意的是当响应头中同时存在Etag和Last-Modified的时候,会先对Etag进行比对,随后才是Last-Modified。
13.1.1 Cache-control
可缓存性
- public:任何地方
- private:只有发起请求的浏览器
- no-cache:任何节点都不可以
到期
- max-age=<seconds>:浏览器默认读取
- s-maxage=<seconds>:代理服务器优先读取
- max-stale=<seconds>:如果有此设置,即便过期了也还可以使用缓存(在发起端设置生效)
重新验证
- must-revalidate
- proxy-revalidate
其他
- no-store:希望代理服务器,去服务器端拿新的
- no-transform :希望代理服务器不要改返回的内容
代理服务器如:Nginx
13.1.2 HTTP头信息控制缓存
Expires
(强缓存)+ 过期时间 :
Expires
是HTTP1.0
提出的一个表示资源过期时间的header
,它描述的是一个绝对时间。
Cache-control
(强缓存):
描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断。管理更有效,安全一些。如:Cache-Control: max-age=3600
服务端返回头Last-Modified/
客户端请求头If-Modified-Since
(协商缓存):
标示这个响应资源的最后修改时间。Last-Modified是服务器相应给客户端的,If-Modified-Sinces是客户端发给服务器,服务器判断这个缓存时间是否是最新的,是的话拿缓存。
服务端返回头Etag
/客户端请求头If-None-Match
(协商缓存):
etag和last-modified类似,用来发送一个字符串来标识版本。
强缓存不请求服务器,客户端判断协商缓存要请求服务器。
注:有时需要将静态资源强制更新,通用的方法是给静态资源文件添加hash(md5)后缀
13.2 浏览器层面
13.2.1 WebStorage
WebStorage是HTML5中新增的本地存储(存储在客户端)的解决方案之一。
WebStorage提供两种类型的API:localStorage和sessionStorage。
为保证使用
localStorage.setItem
等API不报错,使用时尽量加入到try-catch
中,因为某些浏览器是禁用这个 API 的。
localstorage(常用)
localStorage一般用来存储应用数据(不重要但是不经常设置的信息),也可以利用其存储js和css等静态资源。作为一种性能优化的方案,这种方法也曾被大量应用于移动端的网页中。不过缺点也很明显,由于localStorage是保存在本地中的,所以很容易导致 XSS 注入攻击。如果要使用这种方案,一定要做好对应的安全措施。
特点:
- 永久有效
- 存储量增大到 5MB
- 浏览器端(客户端)缓存,不参与和服务器的通信(不会带到 HTTP 请求中)
- API 适用于数据存储
localStorage.setItem(key, value)
localStorage.getItem(key)
sessionStorage
存放一些需要及时失效的重要信息
特点:
- 仅在当前会话下有效,关闭页面或浏览器后被清除
- 存储量增大到 5MB
13.2.2 Cookie
读取、添加和更新文档所关联的 cookie :document.cookie = ....
缺点:
- 存放数据大小:4KB左右(cookie的长度和数量有所限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB。否则会被截掉。)
- 个数限制:不超过20个
- 每次都会携带在HTTP头中,使用cookie保存过多数据会带来性能问题
- 需要自己封装,原生的接口不友好
- 安全性问题
注意事项:
- 通过良好的编程,控制保存在cookie中的session对象的大小。
- 通过加密和安全传输技术,减少cookie被破解的可能性。
- 在cookie中存放不敏感的数据,即使被盗取也不会有很大的损失。
- 控制cookie的生命期,使之不会永远有效。这样的话偷盗者很可能拿到的是一个过期的cookie。
- 有些状态不可以保存在客户端。例如,为了防止重复提交表单,我们需要在服务端保存一个计数器。若把计数器保存在客户端,则起不到什么作用。
13.2.3 IndexedDB
通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
IndexedDB 具有以下特点。
(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
十四、Cookie和Session有什么联系和区别
14.1 Cookie
- 通过Set-Cookie
- 下次请求就会自动带上
- 键值对,可以设置多个
14.1.1 Cookie属性
- max-age和expires:设置过期时间
- Secure:只在https的时候发送
- HttpOnly:无法通过document.cookie访问(禁止重要数据通过JS访问,提高安全性,防止CSRF攻击)
14.2 Cookie 和 Session 的区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能
考虑到减轻服务器性能方面,应当使用cookie。
- session比cookie更安全
cookie不是很安全,可以通过分析存放在本地的cookie并进行cookie欺骗。
- 一般用cookie来存储session id
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
建议:
- 将登录信息等敏感信息存放为session
- 其他信息如果需要保留,可以放在cookie中
十五、内容安全策略(CSP:Content Security Policy)
15.1 作用
- 限制资源获取
- 报告资源获取越权
15.2 限制方式
- default-src限制全局
- 指定资源类型 如:img-src
- report-uri: 检测限制规则
- 建议在HTTP的Header中设置,也可以在meta标签里设置
详见:内容安全策略( CSP ) - HTTP | MDN
十六、HTTPS(HyperText Transfer Protocol over Secure Socket Layer)
HTTPS:主要是HTTP下增加了SSL(安全套接层)或者TSL(传输层安全),在SSL或TSL在传输层对数据进行了加密处理。
HTTPS协议的主要作用:
- 建立一个信息安全通道,来保证数据传输的安全
- 确认网站的真实性。
16.1 HTTP与HTTPS的区别
① HTTPS协议需要到CA申请证书,一般免费证书很少,需要付费。SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
② HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。
③ HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
④ HTTP的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。
16.2 HTTPS如何加密
私钥
解密,放在服务器上
公钥
放在互联网上所有人都能拿到的加密字符串,加密
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤:
(1)客户使用HTTPS的URL访问Web服务器,要求与Web服务器建立SSL连接。
(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web服务器利用自己的私钥解密出会话密钥(主密钥),中间人无法解密。
(6)Web服务器利用会话密钥加密与客户端之间的通信。