title: cookie和session
categories: 计算机网络
tag:
- 计算机网络
date: 2021-12-09 10:05:34
cookie 和 session 和 token
cookie 机制
Cookie 是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。可以实现单点登录
Cookie 的 Domain 和 Path 属性标识了这个 Cookie 是哪一个网站发送给浏览器的;Cookie 的 Expires 属性标识了 Cookie 的有 效时间,当 Cookie 的有效时间过了之后,这些数据就被自动删除了。
如果不设置过期时间,则表示这个 Cookie 生命周期为浏览器会话期间,只要关闭浏览器窗口,Cookie 就消失了。这种生命期为浏览会话期的 Cookie 被称为会话 Cookie。会话 Cookie 一般不保存在硬盘上而是保存在内存里。如果设置了过期时间,浏览器就会把 Cookie 保存到硬盘 上,关闭后再次打开浏览器,这些 Cookie 依然有效直到超过设定的过期时间。存储在硬盘上的 Cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗 口。而对于保存在内存的 Cookie,不同的浏览器有不同的处理方式。(设置 age 为负数就是窗口 cookie,设置 0 删除 cookie,设置正数存活的时间)
使用 cookie 要考虑的问题
因为存储在客户端,容易被客户端篡改,使用前需要验证合法性
不要存储敏感数据,比如用户密码,账户余额
使用 httpOnly 在一定程度上提高安全性
尽量减少 cookie 的体积,能存储的数据量不能超过 4kb
设置正确的 domain 和 path,减少数据传输
cookie 无法跨域
一个浏览器针对一个网站最多存 20 个 Cookie,浏览器一般只允许存放 300 个 Cookie
移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
session 机制
Session 是存放在服务器端的类似于 HashTable 结构(每一种 Web 开发技术的实现可能不一样,下文直接称之为 HashTable)来存放用户 数据,当浏览器第一次发送请求时,服务器自动生成了一个 HashTable 和一个 Session ID 用来唯一标识这个 HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的 Session ID 放在请求中一并发送到服务器上,服务器从请求中提取出 Session ID,并和保存的所有 Session ID 进行对比,找到这个用户对应的 HashTable。
一般情况下,服务器会在一定时间内(默认 20 分钟)保存这个 HashTable,过了时间限制,就会销毁这个 HashTable。在销毁之前,程序员可以将用户的一些数据以 Key 和 Value 的形式暂时存放在这个 HashTable 中。当然,也有使用数据库将这个 HashTable 序列化后保存起来的,这 样的好处是没了时间的限制,坏处是随着时间的增加,这个数据库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。
使用 session 需要考虑的问题
将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session
当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。
当多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。
sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 怎么办? 一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现
Token 令牌
访问资源接口(API)时所需要的资源凭证
简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
特点:
- 服务端无状态化、可扩展性好
- 支持移动端设备
- 安全
- 支持跨程序调用

客户端使用用户名跟密码请求登录
服务端收到请求,去验证用户名与密码
验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
客户端每次向服务端请求资源的时候需要带着服务端签发的 token
服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据
每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库
token 完全由应用管理,所以它可以避开同源策略
Refresh Token
- 另外一种 token——refresh token
- refresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。

Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。
Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。
使用 token 时需要考虑的问题
- 如果你认为用数据库来存储 token 会导致查询时间太长,可以选择放在内存当中。比如 redis 很适合你对 token 查询的需求。
- token 完全由应用管理,所以它可以避开同源策略
- token 可以避免 CSRF 攻击(因为不需要 cookie 了)
- 移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
JWT
JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。
是一种认证授权机制。
JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。
阮一峰老师的 JSON Web Token 入门教程 讲的非常通俗易懂,这里就不再班门弄斧了

JWT 认证流程:
- 用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT
- 客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)
- 当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用 Bearer 模式添加 JWT,其内容看起来是下面这样
Authorization: Bearer <token>
使用 JWT 时需要考虑的问题
因为 JWT 并不依赖 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)
JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
JWT 不加密的情况下,不能将秘密数据写入 JWT。
JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
JWT 最大的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点:由于服务器不需要存储 Session 状态,因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了,到期之前就会始终有效,除非服务器部署额外的逻辑。
JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态。
为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
Token 和 JWT 的区别
相同:
- 都是访问资源的令牌
- 都可以记录用户的信息
- 都是使服务端无状态化
- 都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别:
- Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
- JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
