前言
cookie 是比 token 更早出现的概念。token 的出现其实也是为了弥补 cookie 的不足。那么首先来了解下 cookie 是怎么出现并发展的。
在 web 早期,网站一般都是静态网站,请求的是整个页面。因此 http 协议在最开始设计的时候就是个无状态协议,无论是谁访问网站看到的都是同样的内容。
在 ajax 出现后,前端可以局部请求数据了,网站的功能也越来越复杂多样。出现了前端在查询的时候希望后端记录当前的用户状态的需求。比如用户在输入了用户名和密码登录后,请求添加了一个东西到购物车,再次请求添加到时候后端需要知道这个请求和之前那个添加到购物车的请求是来自同一个,从而展示给用户实际的两个商品。
因为 http 协议本身就是无状态的协议,若想让服务端记住客户端的请求状态,最简单的方法就是客户端每次发请求都把用户名和密码带上,服务端每次都去判断这个用户名和密码再处理请求。
为了解决 http 协议无状态特征带来的不便,cookie 应运而生。
cookie
cookie 是客户端在第一次请求时,服务端下发给客户端的一块数据,客户端(通常是浏览器)将这块数据保存下来,以后的每次请求都需要带上这块数据,服务端通过这块数据来判断这个请求来自哪个客户端。
这个保存 cookie 和每次请求带上 cookie 的行为是自动的。服务端在第一次响应的响应头里会有一个 Set-Cookie
的字段,浏览器在接收到带有这个字段的响应的时候,会自动保存 cookie 到浏览器的 Cookies 内存中,并在之后的请求中自定为请求头上加上 Cookie
的字段,通过这个字段自动带上 cookie 值,服务端解析请求的这个字段就可以判断出该请求是来自哪个客户端了。
cookie 可以理解成是一种请求时浏览器自动携带传递的认证方式。但是根据携带的信息不同形成不同的组合,比如直接携带用户信息、携带 sessionId、携带 token 等。
session
常规情况下,cookie 的信息就是用户相关的信息,比如 userId。因此单纯地 cookie 方式意味着用户信息直接存储在客户端。session 是将用户信息存储在服务端,然后和 sessionId 形成一个映射表,将 sessionId 返回给 cookie。服务端在接受到 cookie 并解析出 session 后,再去 session 里面查询用户信息。
比起 cookie 只能存储字符串,session 可以存储任意数据类型。而且相比于 cookie 存储在客户端,session 存储在服务端更安全。
因此 session 是一种存储用户信息的方式,通常结合 cookie 来使用。二者结合的桥梁是 sessionId
。
token
单纯 cookie 的方式存在的问题是,由于 cookie 是浏览器在发送请求时自动带上的,因此无法跨域使用,同时也容易遭受 csrf 攻击。
cookie + session 的方式存在的问题是,session 是存储在服务端的数据,服务端需要维护这份数据,当数据越来越大时,会给服务端造成负担。
根据以上两个问题,需要的效果就是,认证信息在每次发送请求时手动带上,可以解决跨域无法使用的问题,也可以避免 csrf 攻击。其次,认证信息里携带用户信息、过期时间以及签名等信息。服务端解析这个信息,拿到用户信息过期时间和钱吗等信息,这就是 token,令牌的实现方式。
所谓的 token 无状态性指的是相比于 cookie + session 的方式,token 的方式不必在服务端存储一个信息映射表。信息还是客户端存储,也就是服务端无状态(无存储)。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力。
token 完全由用户控制,可以放在 cookie 里自动传递,也可以手动放在请求头里传递,放在请求头里的时候一般是放在 Authorization
字段中。