1 浏览器访问网页的过程
1.1 HTTP 协议版本
HTTP(HyperText Transfer Protocol)是超文本传输协议
HTTP 是一个在计算机世界里专⻔在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和 规范」
HTTP版本:
-
http/0.9
-
http/1.0
-
http/1.1
-
http/2.0
-
http/3.0
RFC Hypertext Transfer Protocol – HTTP/1.1
https://tools.ietf.org/html/rfc2616
http/0.9:
1991,原型版本,功能简陋,只有一个命令GET。GET /index.html ,服务器只能回应HTML格式字符串,不能回应别的格式
http/1.0
1996年5月,支持cache, MIME, method
每个TCP连接只能发送一个请求,发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接引入了POST命令和HEAD命令头信息是 ASCII 码,后面数据可为任何格式。服务器回应时会告 诉客户端,数据是什么格式,即Content-Type字段的作用。这些数据类型总称为MIME 多用途互联网邮件扩展,每个值包括一级类型和二级类型,预定义的类型,也可自定义类型, 常见Content-Type值: text/xml image/jpeg audio/mp3
http/1.1
1997年1月,引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection:keep-alive
。对于同一个域名,大多数浏览器允许同时建立6个持久连接
引入了管道机制,即在同一个TCP连接里,客户端可以同时发送多个请求,进一步改进了HTTP协议的效率
新增方法:PUT、PATCH、OPTIONS、DELETE
同一个TCP连接里,所有的数据通信是按次序进行的。服务器只能顺序处理回应,前面的回应慢,会有许多请求排队,造成"队头堵塞"(Head-of-line blocking)
为避免上述问题,两种方法:一是减少请求数,二是同时多开持久连接。
网页优化技巧,如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等
HTTP 协议不带有状态,每次请求都必须附上所有信息。请求的很多字段都是重复的,浪费带宽,影响速度
优点是:
-
简单:头部信息都是 key:value 的形式,方便阅读理解
-
灵活和易于扩展,HTTP⼯作在应⽤层( OSI 第七层),则它下层可以随意变化
-
应⽤⼴泛和跨平台
HTTP1.0和HTTP1.1的区别
-
缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略
-
带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如:客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),方便了 开发者自由的选择以便于充分利用带宽和连接
-
错误通知的管理,在HTTP1.1中新增24个状态响应码,如409(Conflict)表示请求的资源与资源当前状态冲突;410(Gone)表示服务器上的某个资源被永久性的删除
-
Host 头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个 虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应 消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
-
长连接,HTTP 1.1支持持久连接(PersistentConnection)和请求的流水线(Pipelining)处理, 在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在 HTTP1.1中默认开启Connection: keep-alive,弥补了HTTP1.0每次请求都要创建连接的缺点
HTTP1.0和1.1的问题
-
HTTP1.x在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出
-
HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,无法保证数据的安全性
-
HTTP1.x在使用时,header里携带的内容过大,增加了传输的成本,并且每次请求header基本不 怎么变化,尤其在移动端增加用户流量
-
虽然HTTP1.x支持了keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间
HTTPS协议
为解决安全问题,网景在1994年创建了HTTPS,并应用在网景导航者浏览器中。最初,HTTP是与SSL一起使用的;在SSL逐渐演变到TLS时(其实两个是一个东西,只是名字不同而已),最新的HTTPS也由在2000年五月公布的RFC2818正式确定下来。HTTPS就是安全版的HTTP,目前大型网站基本实现全站 HTTPS
HTTPS 会话的简化过程
-
客户端发送可供选择的加密方式,并向服务器请求证书
-
服务器端发送证书以及选定的加密方式给客户端
-
客户端取得证书并进行证书验证,如果信任给其发证书的CA
-
验证证书来源的合法性;用CA的公钥解密证书上数字签名
-
验证证书的内容的合法性:完整性验证
-
检查证书的有效期限
-
检查证书是否被吊销
-
证书中拥有者的名字,与访问的目标主机要一致
-
客户端生成临时会话密钥(对称密钥),并使用服务器端的公钥加密此数据发送给服务器,完成密钥交换
-
服务用此密钥加密用户请求的资源,响应给客户端
HTTP 与 HTTPS 的区别
-
HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费
-
HTTP协议运行在TCP之上,所有传输的内容都是明文,存在安全⻛险的问题。HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的
-
HTTP 连接建⽴相对简单, TCP 三次握⼿之后便可进⾏ HTTP 的报⽂传输。⽽ HTTPS 在 TCP 三次握⼿之后,还需进⾏ SSL/TLS 的握⼿过程,才可进⼊加密报⽂传输。
-
HTTP和HTTPS使用的是不同的连接方式,端口不同,前者是80,后者是443
-
HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题
-
HTTPS 实现过程降低用户访问速度,但经过合理优化和部署,HTTPS 对速度的影响还是可以接受的
SPDY协议
SPDY:2009年谷歌研发,综合HTTPS和HTTP两者有点于一体的传输协议,主要特点:
-
降低延迟,针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了带宽的利用率
-
请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有 可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,可以保证用户能第一时间看到网页内容
-
header压缩。HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大 小和数量
-
基于HTTPS的加密协议传输,大大提高了传输数据的可靠性
-
服务端推送(server push),采用了SPDY的网页,例如网页有一个sytle.css的请求,在客户端收 到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了
HTTP2协议
http/2.0:2015年,HTTP2.0是SPDY的升级版
-
头信息和数据体都是二进制,称为头信息帧和数据帧
-
复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,且不用按顺序一一对应,避免了"队头堵塞",此双向的实时通信称为多工(Multiplexing
-
引入头信息压缩机制(header compression),头信息使用gzip或compress压缩后再发送;客户端 和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,不发送同样字段, 只发送索引号,提高速度
-
HTTP/2 允许服务器未经请求,主动向客户端发送资源,即服务器推送(server push)
HTTP2.0和SPDY区别
-
HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
-
HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE
1.2 总结
HTTP 1.0 | HTTP1.1 | HTTP2 | HTTP3 |
---|---|---|---|
采用ASCII字符串的方式传输 | 采用ASCII字符串的方式传输 无状态,明文传输,不安全 | 头信息和数据体都是二进制方式传输, 称为头信息帧和数据帧 HTTP/2 协议是基于 HTTPS 的 所以 HTTP/2 的安全性也是有保障的 | HTTPS 要建⽴⼀个连接,要花费 6 次交互,先是建⽴三次握⼿, 然后是 TLS/1.3 的三次握⼿。QUIC 直接把以往的 TCP 和 TLS/1.3 的 6 次交互合并成了 3 次,减少了交互次数 QUIC 有⾃⼰的⼀套机制可以保证传输的可靠性的。 当某个流发⽣丢包时,只会阻塞这个流,其他流不会受到影响 |
短连接, 对头阻塞 | 长连接 串⾏请求 管道⽹络传输 对头阻塞 没有请求优先级 | 数据流 多路复用及相关功能 消息优先级,解决对头堵塞 | HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP |
头部不压缩,携带cookie | 头部不压缩,携带cookie | 头部压缩 | TLS3 升级成了最新的 1.3 版本,头部压缩算法也升级成了 QPack |
客户端请求,服务端应答 | 客户端请求,服务端应答 | 服务器主动推送 | |
没有host域 | 支持host域 | 支持host域 | 支持host域 |
http协议:stateless(无状态), 服务器无法持续追踪访问者来源
解决http协议无状态的方法
-
cookie(由服务器生成,存放在客户端)
-
session(由服务器生成,存放在服务端)
HTTP事务:HTTP 协议是一个双向协议,一次访问的过程
-
请求:request
-
响应:response
HTTP报文结构
协议查看或分析的工具:tcpdump, wireshark,tshark
参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP
2 HTTP请求报文
报文有三个部分组成,即开始行、首部行和实体主体。在请求报文中,开始行就是请求行
request报文格式
<method><request-URL><version>
<headers>
<entity-body>
示例
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: www.test.com
User-Agent: HTTPie/0.9.4
示例
# post.html
<form action="index.html" method="POST">
username:<br>
<input type="text" name="username" >
<br>
password:<br>
<input type="text" name="password" >
<br><br>
<input type="submit" value="Submit">
</form>
3 HTTP响应报文
响应报文的开始行是状态行
状态行包括三项内容,即 HTTP的版本,状态码,以及解释状态码的简单短语
response报文格式
<version><status><reason-phrase>
<headers>
<entify-body>
示例:
HTTP/1.1 200 OK
Cache-Control: max-age=3, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Date: Thu, 07 Nov 2019 03:44:14 GMT
Server: Tengine
Transfer-Encoding: chunked
Vary: Accept-Encoding
Vary: Accept-Encoding, Cookie
4 ABNF(扩充巴科斯—瑙尔范式)核心规则
规则 | 形式定义 | 意义 |
---|---|---|
ALPHA | %x41-5A / %x61-7A | 大写和小写ASCII字母(A-Z,a-z) |
DIGIT | %x30-39 | 数字(0-9) |
HEXDIG | DIGIT / “A” / “B” / “C” / “D” / “E” / “F” | 十六机制数字(0-9,A-F,a-f) |
DQUOTE | %x22 | 双引号 |
SP | %x20 | 空格 |
HTAB | %x09 | 横向制表符 |
WSP | SP / HTAB | 空格或横向制表符 |
LWSP | *(WSP / CRLF WSP) | 直线空白(晚于换行) |
VCHAR | %x21-7E | 可见(打印)字符 |
CHAR | %x01-7F | 任何7-位US-ASCII字符,不包括NUL(%x00) |
OCTET | %x00-FF | 8位数据 |
CTL | %x00-1F / %x7F | 控制字符 |
CR | %x0D | 回车(MAC的换行) |
LF | %x0A | 换行(Linux的换行) |
CRLF | CR LF | 互联网标准换行(Windows中的换行,因为考虑到了 协议的可见性,所以http协议也用CRLF表示换行) |
BIT | “0” / “1” | 二进制数字 |
5 HTTP报文格式详解
5.1 Method 方法
请求方法,标明客户端希望服务器对资源执行的动作,包括以下:
header 1 | header 2 |
---|---|
GET | 从服务器获取一个资源,主要的获取信息方法,大量的性能优化都针对该方法,幂等方法 |
POST | 向服务器输入数据,通常会再由网关程序继续处理,常用于提交 HTML FORM 表单、新增资源等 |
HEAD | 类似 GET 方法,但服务器不发送 BODY,用以获取 HEAD 元数据(只从服务器获取文档的响应首部),幂等方法 |
PUT | 将请求的主体部分存储在服务器中,如上传文件,更新资源,带条件时是幂等方法 |
DELETE | 请求删除服务器上指定的文档,幂等方法 |
TRACE | 追踪请求到达服务器中间经过的代理服务器,回显服务器收到的请求, 用于定位问题。有安全风险(2007年4月2号之后就不在支持了,返回405错误码) |
OPTIONS | 显示服务器对访问资源支持的方法,主要用于跨域访问,幂等方法curl static.taohui.tech -X OPTIONS -I |
CONNECT | 建立一个到由目标资源标识的服务器的 tunnel 隧道 |
PATCH | 用于对资源应用部分修改 |
GET 和 POST 的区别
-
GET
⽅法的含义是请求从服务器获取资源,这个资源可以是静态的⽂本、⻚⾯、图⽚视频等 -
POST
向 URI 指定的资源提交数据,数据就放在报⽂的 body ⾥ -
GET
⽅法就是安全且幂等的 -
POST
因为是新增或提交数据,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的
5.2 version版本
HTTP/<major>.<minor>
示例
HTTP/1.1
5.3 status 状态码
三位数字,标记请求处理过程中发生的情况
参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
http协议状态码分类
header 1 | header 2 |
---|---|
1xx | 信息提示 |
2xx | 成功 |
3xx | 重定向 |
4xx | 错误类信息,客户端错误 |
5xx | 错误类信息,服务器端错误 |
5.4 reason-phrase原因短语
状态码所标记的状态的简要描述
5.5 headers首部字段头
首部字段包含的信息最为丰富。首部字段同时存在于请求和响应报文内,并涵盖 HTTP 报文相关的内容
信息。使用首部字段是为了给客服端和服务器端提供报文主体大小、所使用的语言、认证信息等内容
首部字段是由首部字段名和字段值构成的,中间用冒号":”分隔字段值对应,即key/value 键/值对
单个 HTTP 首部字段可以有多个值
参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers
首部的分类
- 通用首部:请求报文和响应报文两方都会使用的首部
- 请求首部:从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、请求内容相关优先级等信息
- 响应首部:从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息
- 实体首部:针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的的信息
- 扩展首部
通用首部:
-
Date: 报文的创建时间
-
Connection:连接状态,常⽤于客户端要求服务器使⽤ TCP 持久连接,以便其他请求复⽤,HTTP/1.1 版本的默认连接都是持久连接,但为了兼容⽼版本的 HTTP,需要指定
Connection
⾸部字段的值为
Keep-Alive
。Connection: keep-alive
⼀个可以复⽤的 TCP 连接就建⽴了,直到客户端或服务器主动关闭连接。但是,这不是标准字段 -
Via:显示报文经过的中间节点(代理,网关)
-
Cache-Control:控制缓存,如缓存时长
-
MIME-Version:发送端使用的MIME版本
-
Warning:错误通知
请求首部
-
Accept:通知服务器自己可接受的媒体类型
-
Accept-Charset: 客户端可接受的字符集
-
Accept-Encoding:客户端可接受编码格式,如gzip
-
Accept-Language:客户端可接受的语言
-
Client-IP: 请求的客户端IP
-
Host: 请求的服务器名称和端口号,客户端发送请求时,⽤来指定服务器的域名,如
Host: www.test.com
。 -
Referer:跳转至当前URI的前一个URL
-
User-Agent:客户端代理,浏览器版本
条件式请求首部
-
Expect:允许客户端列出某请求所要求的服务器行为
-
If-Modified-Since:自从指定的时间之后,请求的资源是否发生过修改
-
If-Unmodified-Since:与上面相反
-
If-None-Match:本地缓存中存储的文档的ETag标签是否与服务器文档的Etag不匹配
-
If-Match:与上面相反
安全请求首部
-
Authorization:向服务器发送认证信息,如账号和密码
-
Cookie: 客户端向服务器发送cookie
代理请求首部
- Proxy-Authorization: 向代理服务器认证
响应首部
信息性:
-
Age:从最初创建开始,响应持续时长
-
Server:服务器程序软件名称和版本
协商首部:某资源有多种表示方法时使用
-
Accept-Ranges:服务器可接受的请求范围类型
-
Vary:服务器查看的其它首部列表
安全响应首部:
-
Set-Cookie:向客户端设置cookie
-
WWW-Authenticate:来自服务器对客户端的质询列表
实体首部
-
Allow: 列出对此资源实体可使用的请求方法
-
Location:告诉客户端真正的实体位于何处
-
Content-Encoding:对主体执行的编码,表示数据的压缩⽅法。表示服务器返回的数据使⽤了什么压缩格式,如:
Content-Encoding: gzip
表示服务器返回的数据采⽤了 gzip ⽅式压缩,告知客户端需要⽤此⽅式解压,客户端在请求时,⽤Accept-Encoding
字段说明⾃⼰可以接受哪些压缩⽅法,如:Accept-Encoding: gzip, deflate
-
Content-Language:理解主体时最适合的语言
-
Content-Length: 主体的长度,服务器在返回数据时,会有
Content-Length
字段,表明本次回应的数据⻓度,如:Content-Length: 100
-
Content-Location: 实体真正所处位置
-
Content-Type:主体的对象类型,⽤于服务器回应时,告诉客户端,本次数据是什么格式。客户端请求的时候,可以使⽤
Accept
字段声明⾃⼰可以接受哪些数据格式,如:Accept: */*
表示客户端声明自己可以接受任何格式的数据
缓存相关
-
ETag:实体的扩展标签
-
Expires:实体的过期时间
-
Last-Modified:最后一次修改的时间
5.6 entity-body实体
请求时附加的数据或响应时附加的数据,例如:登录网站时的用户名和密码,博客的上传文章,论坛上的发言等。
6 Cookie 和 Session
无状态协议是指协议对事物处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它应答就很快。
HTTP是超本文传输协议,顾名思义,这个协议支持超文本的传输。什么是超文本?说白了就是使用HTML编写的页面。通常,我们使用客户端浏览器访问服务器的资源,最常见的URL也是以html为后缀的文件,因此可以说超文本是网络上最主要的资源。
既然HTTP协议的目的是在于支持超文本的传输,也就是资源的传输,那么客户端浏览器向HTTP服务器发送请求,继而HTTP服务器将相应资源发回给客户端,这样一个过程中,无论对于客户端还是服务器,都没有必要记录这个过程,因为每一次请求和响应都是相对独立的,一般而言,一个URL对应着一个唯一的超文本,正是因为这样的唯一性,使得记录用户的行为状态变得毫无意义,所以,HTTP协议被设计为无状态的连接协议符合它本身的需求。
HTTP协议这种特性有优点也有缺点,优点在于解放了服务器,每一次请求"点到为止",不会造成不必要的连接占用,缺点在于如果为了保留状态,每次请求都会传输大量的重复信息内容。
可是随着 Web 的不断发展,很多业务都需要对通信状态进行保存.
如果是一次性会话的过程: 打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器
但目前有很多WEB访问场景,并不是一次性会话,而是多次相关的会话,比如:
登录场景:
打开浏览器 -> 浏览到登陆页面 -> 输入用户名和密码 -> 访问到用户主页(显示用户名) -> 修改密码(输入 原密码)-> 修改收货地址…
问题:在此处登录会话过程中产生的数据(用户会话数据)如何保存下来呢?
购物场景:
打开浏览器 -> 浏览商品列表 -> 加入购物车(把商品信息保存下来) -> 关闭浏览器 打开浏览器-> 直接进入购物车 ->查看到上次加入购物车的商品 -> 下订单 -> 支付
问题: 在购物会话过程中,如何保存商品信息?
以上场景都需要保留会话数据,需要会话管理机制
会话管理: 管理浏览器客户端和服务器端之间会话过程中产生的会话数据。
为了会话管理,HTTP就需要传输大量重复信息内容的问题,造成大量的网络带宽消耗。于是 Cookie 和 Session 技术闪亮登场了,它们可以为用户进行会话管理,实现保存状态。
6.1 Cookie
Cookie 又称为"小甜饼”。类型为"小型文本文件”,指某些网站为了辨别用户身份而储存在用户本地终端 (Client Side)上的数据(通常经过加密)。由网景公司的前雇员卢·蒙特利在1993年3月发明
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么,所以Cookie就是用来绕开HTTP的无状态性的"额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。
在上面的购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段Cookie,记录着那项商品的信息。当用户访问另一个页面,浏览器会把Cookie发送给服务器,于是服务器知道他之前选购了什么。用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。结帐时,服务器读取发送来的Cookie就行了。
Cookie基于HTTP协议,也叫Web Cookie或浏览器Cookie,是服务器发送到用户浏览器并保存在客户端本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP 协议记录稳定的状态信息成为了可能
cookie 的获取过程
第一次请求过程
浏览器第一次发送请求时,不会携带任何cookie信息
服务器接收到请求之后,发现请求中没有任何cookie信息
服务器生成和设置一个cookie。并将此cookie设置通过set_cookie的首部字段保存在响应报文中返回给浏览器
浏览器接收到这个响应报文之后,发现里面有cookie信息,浏览器会将cookie信息保存起来
第二次及其之后的过程
当浏览器第二次及其之后的请求报文中自动cookie的首部字段携带第一次响应报文中获取的cookie信息
服务器再次接收到请求之后,会发现请求中携带的cookie信息,这样的话就认识是谁发的请求了
之后的响应报文中不会再添加set_cookie首部字段
Cookie主要用于以下三个方面:
-
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
-
个性化设置(如用户自定义设置、主题等)
-
浏览器行为跟踪(如跟踪分析用户行为等)
使用 Cookie 的状态管理
Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。当服务器收到HTTP请求时,服务器可以在响应头里面添加一个Set-Cookie选项。浏览器收到响应后通常会保存下Cookie,之后 对该服务器每一次请求中都通过Cookie请求头部将Cookie信息发送给服务器。服务器端发现客户端发送 过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息.另外,Cookie的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。
Set-Cookie首部字段
-
NAME=VALUE 赋予 Cookie 的名称和其值,此为必需项
-
expires=DATE Cookie的有效期,若不明确指定则默认为浏览器关闭前为止
会话期Cookie
基于内存保存,会话期Cookie是最简单的Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。会话期Cookie不需要指定过期时间(Expires)或者有效期(Max-Age)。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期Cookie也会被保留下来,就好像浏览器从来没有关闭一样。持久性Cookie
基于硬盘保存,和关闭浏览器便失效的会话期Cookie不同,持久性Cookie可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
提示:当Cookie的过期时间被设定时,设定的日期和时间只与客户端相关,而不是服务端
-
path=PATH 指定了主机下的哪些路径可以接受Cookie(该URL路径必须存在于请求URL中)。若不指定则默认为文档所在的文件目录,以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。
例如:设置
Path=/docs
,则以下地址都会匹配:/docs /docs/Web/ /docs/Web/HTTP
-
domain=域名 指定了哪些主机可以接受Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了Domain,则一般包含子域名。
例如,如果设置Domain=test.com
,则Cookie也包含子域名(如:str.test.com
) -
Secure 标记为 Secure 的Cookie只应通过被HTTPS协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure 标记也无法提供确实的安全保障。从 Chrome 52 和 Firefox 52 开始,不安全的站点(http:)无法使用Cookie的 Secure 标记。
-
HttpOnly 加以限制使 Cookie 不能被 JavaScript 脚本访问,为避免跨域脚本 (XSS) 攻击,通过 JavaScript的 Document.cookie API无法访问带有 HttpOnly 标记的Cookie,它们只应该发送给服 务端。如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为 其设置 HttpOnly 标记
浏览器对cookie的限制
Cookie 存储的限制是不一样的。例如:单个域名可存储的 Cookie 数量、Cookie 大小等。
null | IE6.0 | IE7.0/8.0 | Opera | FF | Safari | Chrome |
---|---|---|---|---|---|---|
cookie个数 | 每个域为20个 | 每个域为50个 | 每个域为30个 | 每个域为50个 | 没有个数限制 | 每个域为53个 |
cookie大小 | 4095个字节 | 4095个字节 | 4096个字节 | 4097个字节 | 4097个字节 | 4097个字节 |
在进行页面 Cookie 操作的时候,应该尽量保证 Cookie 的个数小于 20 个,总大小小于 4KB,这是一个 安全且保险的范围。
示例:响应报文中的set-cookie首部
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
示例:请求报文中的cookie首部字段
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
示例:响应报文set-cookie中的Secure 和 HttpOnly
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
示例:浏览器查看cookie
示例:php语言实现cookie的管理
#设置cookie
#cat setcookie.php
<?php
setcookie('title','ceo'); #有效期为会话级
setcookie('user','song',time()+3600*12); #有效期为12小时
echo "<h1>test setcookie </h1>"
?>
#说明:setcookie设置的cookie,只有下一次http请求才能生效
#显示cookie
cat showcookies.php
<?php
echo "<h1>test showcookie </h1>";
echo $_COOKIE["user"]; #显示user的这一个cookie
echo "<br />";
var_dump($_COOKIE); #显示所有cookie
//print_r($_COOKIE); #不如上面方式详细
?>
#删除cookie,通过设置过期时间实现
#vim delcookie.php
<?php
setcookie('user','song',time()-3600*12);
echo "<h1>cookie:user is deleted </h1>";
?>
6.2 Session
session是相对于cookie的另外一个状态保持的解决方案,它是通过服务器来保持状态的。
session指的是服务器上为每个客户端所开辟的独立存储空间,在其中保存的信息就是用于保存状态的。
Session是服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建session的方法。在创建了session的同时,服务器会为该session生成唯一的sessionId,而这个sessionId被创建了之后,就可以调用session相关的方法往session中增加内容了,而这些内容只会保存在服务器中,每个sessionid就像数据库中主键,可以根据SessionId 关联每个session的相关信息,比如:购物车里的商品,登录用户等。但发送给客户端浏览器的只有sessionId。当客户端浏览器再次发送http请求时,会自动地将这个sessionId 附加在请求报文中 ,服务器收到请求之后就会根据sessionId找到对应的session,从而再次使用,使得用户的状态得以保持。
每个session都有一个sessionId,这个ID存放有两种方式
-
通过URL存取,比如:Java程序中,URL会带上一个jsessionId=xxxxxx等,这样每次重新请求的时候都传了sessionId给服务器,但此方式不安全,所以很少使用,所以一般session是依赖于cookie的.即如果浏 览器禁用了cookie,则session无法实现
-
通过cookie存取(Tomcat默认如此),这种cookie是session cookie,区别于persistent cookies也就是我们常说的cookie,session cookie要注意的是存储在浏览器内存中,而不是写到硬盘上。程序一开始执行,服务器就生成一个sessionId并通过cookie携带客户端浏览器的缓存中,当下一次访问的时候,服务器先检测一下是否有这个cookie,如果有就取它的ID,如果没有就再生成一个。这就是为什么 关闭浏览器之后,再进去session已经没有了,其实在服务器端session并没有清空,而是sessionId变了。
当将浏览器关闭,服务器保存的session数据不是立即释放的,此时数据还会存在一段时间(可以在程序中加以设置,Tomcat默认15分钟),只要我们知道那个sessionId,就可以继续通过请求获得此session 的信息。session里面的数据都放在服务器端,通过sessionId保证不会访问错误,服务端自动对session进行管理,如果在规定的时间内没有访问,则释放掉这个session。
最后提两点:
-
sessionId通常在浏览器地址中是看不到的,但是当我们把浏览器的cookie禁止之后,Web服务器会采用URL重写的方式传递sessionId,这样就可以在地址栏看到sessionId了
-
session cookie不可以跨窗口使用,但可以跨同一个窗口的多个标签页。
session 的工作流程
第一次请求:
-
浏览器发起第一次请求的时候可以携带一些信息(比如:用户名/密码) cookie中没有任何信息
-
当服务器接收到这个请求之后,进行用户名和密码的验证,验证成功后则可以设置session信息
-
在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个随机的 session id 的cookie信息
-
客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存session id的信息)
第二次及其之后的请求
-
第二次及其之后的请求都会携带session id信息
-
当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证
-
验证成功,则可以获取session信息(session信息保存在服务器端)
示例:PHP的PHPSESSID
<?php
session_start();
echo session_id();
?>
#执行结果如下图
示例:JAVA的JSESSIONID
[root@centos8 ~]#cat /usr/local/tomcat/webapps/ROOT/session.jsp
<h1>
sessionId: <%=request.getSession().getId()%>
</h1>
6.3 cookie和session比较
cookie和session的相同和不同
cookie | session |
---|---|
cookie通常是在服务器生成,但也可以在客户端生成 | session是在服务器端生成的 |
cookie 将数据保存在客户端的内存或文件中 | session 将数据信息保存在服务器端,可以是内存,文件,数据库等多种形式 |
单个cookie保存的数据不能超过4K,每个站点cookie个数有限制,比如IE8为50个、Firefox为50 个、Opera为30个 | session存储在服务器,没有容量限制 |
cookie存放在用户本地,可以被轻松访问和修改,安全性不高 | session存储于服务器,比较安全 |
cookie有会话cookie和持久cookie,生命周期为浏览器会话期的会话cookie保存在缓存,关闭浏 览器窗口就消失,持久cookie被保存在硬盘,直到超过设定的过期时间 | 随着服务端session存储压力增大,会根据需要定期清理session数据 |
服务器会将cookie设置通过set_cookie的首部字段保存在响应报文中返回给浏览器服务器再次接收到请求之后,会发现请求中携带的cookie信息,这样的话就认识是谁发的请求了 | session中有众多数据,只将sessionID这一项可以通过cookie发送至客户端进行保留,客户端下次 访问时,在请求报文中的cookie会自动携带sessionID,从而和服务器上的的session进行关联 |
cookie缺点
-
使用cookie来传递信息,随着cookie个数的增多和访问量的增加,它占用的网络带宽也很大,试想假如cookie占用200字节,如果一天的PV有几个亿,那么它要占用多少带宽?
-
cookie并不安全,因为cookie是存放在客户端的,所以这些cookie可以被访问到,设置可以通过插件添加、修改cookie。所以从这个角度来说,我们要使用sesssion,session是将数据保存在服务端的, 只是通过cookie传递一个sessionId而已,所以session更适合存储用户隐私和重要的数据
session 缺点
-
不容易在多台服务器之间共享,可以使用session绑定,session复制,session共享解决
-
session存放在服务器中,所以session如果太多会非常消耗服务器的性能
总结:cookie和session各有优缺点,在大型互联网系统中,单独使用cookie和session都是不可行的