1 、概述
我们都知道 HTTP 协议是无状态的,所谓的无状态就是客户端每次想要与服务端通信,都必须重新与服务端链接,意味着请求一次客户端和服务端就连接一次,下一次请求与上一次请求是没有关系的。
这种无状态的方式就会存在一个问题:如何判断两次请求的是同一个人?就好比用户在页面 A 发起请求获取个人信息,然后在另一个页面同样发起请求获取个人信息,我们如何确定这俩个请求是同一个人发的呢?
为了解决这种问题,我们就迫切需要一种方式知道发起请求的客户端是谁?此时,cookie、token、session 就出现了,它们就可以解决客户端标识的问题,在扩大一点就是解决权限问题。
它们就好比让每个客户端或者说登录用户有了自己的身份证,我们可以通过这个身份证确定发请求的是谁。
2、什么是cookie
2.1 概念
Cookie 是一种在客户端存储数据的技术,它是由服务器发送给客户端的小型文本文件,存储在客户端的浏览器中,大小限制大致在 4KB 左右。在客户端发送请求时,浏览器会自动将相应的 Cookie 信息发送给服务器,服务器通过读取 Cookie 信息,就可以判断该请求来自哪个客户端。Cookie 可以用于存储用户的登录状态、购物车信息等。
在以前很多开发人员通常用 cookie 来存储各种数据,后来随着更多浏览器存储方案的出现,cookie 存储数据这种方式逐渐被取代,主要原因有如下:
- cookie 有存储大小限制,4KB 左右。
- 浏览器每次请求会携带 cookie 在请求头中。
- 字符编码为 Unicode,不支持直接存储中文。
- 数据可以被轻易查看。
2.2 cookie 的主要属性
| 属性名 | 描述 |
|---|---|
| name | cookie 的名称 |
| value | cookie 的值 |
| comment | cookie 的描述信息 |
| domain | 可以访问该 cookie 的域名 |
| expires | cookie 的过期时间,具体某一时间 |
| maxAge | cookie 的过期时间 |
| path | cookie 的使用路径 |
| secure | cookie 是否使用安全协议传输,比如 SSL 等 |
| version | cookie 使用的版本号 |
| isHttpOnly | 指定该 Cookie 无法通过 JavaScript 脚本拿到,比如 Document.cookie 属性、XMLHttpRequest 对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。 |
2.3 使用流程
那么我们是如何通过 cookie 来实现用户确定或者权限的确定呢?看下图:

接下来说说大体流程
- 客户端发送请求到服务端(比如登录请求)。
- 服务端收到请求后生成一个 session 会话。
- 服务端响应客户端,并在响应头中设置 Set-Cookie。Set-Cookie 里面包含了 sessionId,它的格式如下:Set-Cookie: value [; expires=date] [; domain=domain] [; path=path] [; secure] 。其中 sessionId 就是用来标识客户端的,类似于去饭店里面,服务员给你一个号牌,后续上菜通过这个号牌来判断上菜到哪里。
- 客户端收到该请求后,如果服务器给了 Set-Cookie,那么下次浏览器就会在请求头中自动携带 cookie。
- 客户端发送其它请求,自动携带了 cookie,cookie 中携带有用户信息等。
- 服务端接收到请求,验证 cookie 信息,比如通过 sessionId 来判断是否存在会话,存在则正常响应。
2.4 cookie 主要特点
- cookie 存储在客户端
- cookie 不可跨域,但是在如果设置了 domain,那么它们是可以在一级域名和二级域名之间共享的。
3、什么是 session
3.1 概念
session 由服务端创建,当一个请求发送到服务端时,服务器会检索该请求里面有没有包含 sessionId 标识,如果包含了 sessionId,则代表服务端已经和客户端创建过 session,然后就通过这个 sessionId 去查找真正的 session,如果没找到,则为客户端创建一个新的 session,并生成一个新的 sessionId 与 session 对应,然后在响应的时候将 sessionId 给客户端,通常是存储在 cookie 中。如果在请求中找到了真正的 session,验证通过,正常处理该请求。
每一个客户端与服务端连接,服务端都会为该客户端创建一个 session,并将 session 的唯一标识 sessionId 通过设置 Set-Cookie 头的方式响应给客户端,客户端将 sessionId 存到 cookie 中。
3.2 流程图
通常情况下,cookie 和 session 都是结合着来用,当然你也可以单独只使用 cookie 或者单独只使用 session,这里我们就将 cookie 和 session 结合着来用。如下图,具体流程介绍看上面cookie流程即可。

3.3 与 cookie 的区别
cookie 和 session,它们两者之间主要是通过 sessionId 关联起来的,所以总结出:sessionId 是 cookie 和 session 之间的桥梁。
session 是基于 cookie 实现的,它们两个主要有以下特点:
- session 比 cookie 更加安全,因为它是存在服务端的,cookie 是存在客户端的。
- cookie 只支持存储字符串数据,session 可以存储任意数据。
- cookie 的有效期可以设置较长时间,session 有效期都比较短。
- session 存储空间很大,cookie 有限制。
系统想要实现鉴权,可以单独使用 cookie,也可以单独使用 session,但是建议结合两者使用。
4、token 是什么
4.1 概念
Token 是一种在客户端和服务端之间传递身份信息的方式。当用户登录成功后,服务端会生成一个 Token,将其发送给客户端。客户端在后续的请求中,需要将 Token 携带在请求头或请求参数中。服务端通过验证 Token 的合法性,就可以确定该请求来自哪个用户,并且可以根据用户的权限进行相应的操作。Token 可以有效地避免了 Cookie 的一些安全问题,比如 CSRF 攻击。
4.2 token 的组成
Token是一个由一串字符组成的令牌,用于在计算机系统中进行身份验证和授权。它通常由三个部分组成:标头、有效载荷、签名。
- 标头( Header ):包含了算法和类型,用于指定如何对有效载荷进行编码和签名。常用的算法有HMAC、RSA、SHA等。
- 有效载荷( Payload ):包含了一些信息,如用户ID、角色、权限等,用于验证身份和授权。有效载荷可以是加密的,也可以是明文的。
- 签名( Signature ):是对标头和有效载荷进行签名后得到的值,用于验证 token 的完整性和真实性。签名通常使用私钥进行签名,并使用公钥进行验证。
一个完整的 token 包含了标头、有效载荷和签名三个部分,它们一起构成了一个安全的令牌,用于进行身份验证和授权。
4.3 token 认证流程
- 客户端发起登录请求,比如用户输入用户名和密码后登录。
- 服务端校验用户名和密码后,将用户 id 和一些其它信息进行加密,生成 token。
- 服务端将 token 响应给客户端。
- 客户端收到响应后将 token 存储下来。
- 下一次发送请求后需要将 token 携带上,比如放在请求头中或者其它地方。
- 服务端 token 后校验,校验通过则正常返回数据。
看下图:

5、三者对比
cookie、session、token 三者最终的目的都是一样:鉴权和认证,下面使用表格的方式直观的描述下三者的优缺点。
| 方式 | 特点 | 优点 | 缺点 |
|---|---|---|---|
| cookie | 1、存储在客户端。 2、请求自动携带 cookie。 3、存储大小 4KB。 |
1、兼容性好,因为是比较老的技术。 2、很容易实现,因为 cookie 会自动携带和存储。 |
1、需要单独解决跨域携带问题,比如多台服务器如何共享 cookie。 2、会遭受 CSRF 攻击。 3、存储在客户端,不够安全。 |
| session | 1、存储在服务端。 2、存储大小无限制。 |
1、查询速度快,因为是个会话,相当于是在内存中操作。 2、结合 cookie 后很容易实现鉴权。 3、安全,因为存储在服务端。 |
1、耗费服务器资源,因为每个客户端都会创建 session。 2、占据存储空间,session 相当于存储了一个完整的用户信息。 |
| token | 1、体积很小。 2、自由操作存储在哪里。 |
1、安全,因为 token 一般只有用户 id,就算被截取了也没什么用。 2、无需消耗服务器内存资源,它相当于只存了用户 id,session 相当于存储了用户的所有信息。 3、跨域处理较为方便,比如多台服务器之间可以共用一个 token。 |
1、查询速度慢,因为 token 只存了用户 id,每次需要去查询数据库。 |
PS:总结下来就是session 是空间换时间,token 是时间换空间。