参考资料:
1 为什么提出 Cookie?HTTP 无状态
HTTP 是无状态的。你这次访问完服务器后,再次访问服务器,服务器是不知道又是你来访问的。
百度百科:HTTP 无状态
- HTTP 无状态协议,是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
- HTTP 的每次请求都是独立的,它的应答情况与前后请求是无直接关系的,它不会受到前面请求的应答情况的直接影响,也不会直接影响后面请求的应答情况。
保持登录状态
- 通常,用户名和密码都保存在数据库中。每次登录的时候,比对用户输入的用户名、密码和数据库中的用户名、密码,比对成功就返回相应的页面。而如果我们想要保持登录状态,其核心就是存储用户名、密码,也就是允许记住用户名、密码。
- 假设我们就让浏览器来记住用户名、密码,那么我们还需要解决 HTTP 无状态的问题,也就是让浏览器想办法在每一次 HTTP 请求中都加入用户名、密码。而 Cookie 就是一种能够实现每次 HTTP 请求都自动携带数据给服务器的技术。
2 Cookie
Cookie 的基本流程如下图所示:
- 浏览器发起 HTTP 请求,服务器会使用 Set-Cookie 字段设置 Cookie;
- Cookie 中有 Name 和 Value 两个属性,均由服务器来进行填充;
- 浏览器收到 Cookie 后,会将 Cookie 存储起来;
- 浏览器以后每次发送 HTTP 请求都会自动携带该 Cookie;
我们可以直接在浏览器中查看存储了哪些 Cookie,由此可见把用户名、密码存储在 Cookie 当中是很不安全的。只要电脑被黑,Cookie 中的重要信息就会被泄露。因此,大家提出了一种新的技术 —— Session 会话。
个人感觉上图只是为了说明 Cookie 的作用,并没有代入存储用户名、密码的场景。
3 Session
Session 的基本流程如下图所示:
可以看出 Cookie 其实是一种数据的载体,它和 Session 并不是一种同级的关系。Cookie 里面可以存放一次 Session 会话的各种参数,并在后续的 HTTP 请求中自动进行携带。
- 会话的开始:浏览器访问服务器时;
- 会话的结束:服务器为该会话设置的结束时间;
服务器为与每个用户的会话都设置了唯一的 SessionID 和结束会话的时间 Max-age 。由于 SessionID 和 Max-age 都是服务器自己定义的东西,因此一般都存储在数据库中。
- 浏览器将登录时填写的用户名、密码发送给服务器;
- 服务器验证身份成功,创建 SessionID 和 Max-age 等参数;
- 服务器将 SessionID 和 Max-age 等参数放入 Cookie 中,其中 Cookie 的有效期被设置为 Max-age,然后发送给浏览器;
- 浏览器收到 Cookie 后对 SessionID 和 Max-age 等参数进行保存;
浏览器保存 Cookie 后,之后的每个 HTTP 请求都会自动把 Cookie 发送给服务器,即每次都会把 SessionID 发送给服务器。当 Cookie 的有效期到期时,浏览器会自动对其进行删除,代表着会话的结束。下一次用户登录时,就需要重新输入用户名、密码了。
由于在 Session 中浏览器保存的是 SessionID,即一个没有规律的字符串,而非用户名、密码,因此即使电脑被黑,黑客也无法获取到用户名、密码。此外,服务器在发送 Cookie 之前会对 Cookie 进行签名,一旦黑客修改了 SessionID,就会变成服务器无法识别的字符串。
4 Token
为什么使用 Token?
在特定时间有大量用户访问服务器的时候,服务器将要面临存储大量 SessionID 的情况。如果拥有多台服务器,那么为了避免某台服务器超载,可能会面临把 SessionID 分享给其他服务器的问题,即把一些用户分配给其他服务器。一直让服务器这样分享也不是办法,于是可以让数据库存储 SessionID,但是如果数据库崩溃了又会影响服务器获取 SessionID 。在各种原因和需求的前提下,就出现了一种技术叫 JWT,即 JSON Web Token 。
Token 的简介
Token 的基本流程如下图所示:
- 浏览器将登录时填写的用户名、密码发送给服务器;
- 服务器生成一个 JWT,服务器保存的不是 JWT 本身,而是 JWT 签名的密文;
- 服务器将 JWT 发送给浏览器;
- 浏览器以 Cookie 或者 Storage 的形式存储 JWT;
JWT 由三部分组成:
h e a d e r . p a y l o a d . s i g n a t u r e \mathrm{header.payload.signature} header.payload.signature
- h e a d e r \mathrm{header} header 部分声明使用什么算法来生成签名
- p a y l o a d \mathrm{payload} payload 部分是特定的数据,比如:有效期等
- s i g n a t u r e \mathrm{signature} signature 部分是签名信息
首先对 h e a d e r \mathrm{header} header 部分和 p a y l o a d \mathrm{payload} payload 部分进行 Base64 编码,然后使用 h e a d e r \mathrm{header} header 部分声明的算法对这两部分进行签名,从而得到 s i g n a t u r e \mathrm{signature} signature 部分。
JWT 在线演示网站:https://jwt.io/
5 总结
- Cookie 是一种数据载体,Session 和 Token 都可以放入其中;
- Session 诞生并保存在服务器中;
- Token 诞生在服务器,但是保存在浏览器中。