Cookies 和 Session
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。 Cookie 主要用于以下三个方面:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
Cookie 在 Web 应用中经常承担标识请求方身份的功能,所以 Web 应用在 Cookie 的基础上封装了 Session 的概念,专门用做用户身份识别。
适用范围
@midwayjs/web
下(即 egg)内置的是 egg 自带的 Cookie,未提供替换能力,不适用本文档@midwayjs/express
下(即 express)内置的是 express 自带的 Cookie 库,未提供替换能力,不适用本文档
默认的 Cookies
Midway 提供了 @midwayjs/cookies
模块来操作 Cookie。
同时在 @midwayjs/koa
中,默认提供了从上下文直接读取、写入 cookie 的方法
ctx.cookies.get(name, [options])
读取上下文请求中的 cookiectx.cookies.set(name, value, [options])
在上下文中写入 cookie
示例如下:
import { Inject, Controller, Get, Provide } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;
@Get('/')
async home() {
// set cookie
this.ctx.cookies.set('foo', 'bar', { encrypt: true });
// get cookie
this.ctx.cookies.get('foo', { encrypt: true });
}
}
设置 Cookie
使用 ctx.cookies.set(key, value, options)
API 来设置 Cookie。
设置 Cookie 其实是通过在 HTTP 响应中设置 set-cookie 头完成的,每一个 set-cookie 都会让浏览器在 Cookie 中存一个键值对。在设置 Cookie 值的同时,协议还支持许多参数来配置这个 Cookie 的传输、存储和权限。
这些选项包括:
选项 | 类型 | 描述 | 支持版本 |
---|---|---|---|
path | String | 设置键值对生效的 URL 路径,默认设置在根路径上(/ ),也就是当前域名下的所有 URL 都可以访问这个 Cookie。 | |
domain | String | 设置键值对生效的域名,默认没有配置,可以配置成只在指定域名才能访问。 | |
expires | Date | 设置这个键值对的失效时间,如果设置了 maxAge,expires 将会被覆盖。如果 maxAge 和 expires 都没设置,Cookie 将会在浏览器的会话失效(一般是关闭浏览器时)的时候失效。 | |
maxAge | Number | 设置这个键值对在浏览器的最长保存时间。是一个从服务器当前时刻开始的毫秒数。如果设置了 maxAge,expires 将会被覆盖。 | |
secure | Boolean | 设置键值对 只在 HTTPS 连接上传输,框架会帮我们判断当前是否在 HTTPS 连接上自动设置 secure 的值。 | |
httpOnly | Boolean | 设置键值对是否可以被 js 访问,默认为 true,不允许被 js 访问 | |
partitioned | Boolean | 设置独立分区状态(CHIPS)的 Cookie。注意,只有 secure 为 true 且 Chrome >=114 版本此配置才会生效 | @midwayjs/cookies >= 1.1.0 |
removeUnpartitioned | Boolean | 是否删除非独立分区状态的同名 cookie。注意,只有 partitioned 为 true 的时候此配置才会生效 | @midwayjs/cookies >= 1.2.0 |
priority | String | 设置 Cookie 的 优先级,可选值为 Low 、Medium 、High ,仅对 Chrome >= 81 版本有效 | @midwayjs/cookies >= 1.1.0 |
除了这些属性之外,框架另外扩展了 3 个参数:
选项 | 类型 | 描述 |
---|---|---|
overwrite | Boolean | 设置 key 相同的键值对如何处理,如果设置为 true,则后设置的值会覆盖前面设置的,否则将会发送两个 set-cookie 响应头。 |
signed | Boolean | 设置是否对 Cookie 进行签名,如果设置为 true,则设置键值对的时候会同时对这个键值对的值进行签名,后面取的时候做校验,可以防止前端对这个值进行篡改。默认为 true。 |
encrypt | Boolean | 设置是否对 Cookie 进行加密,如果设置为 true,则在发送 Cookie 前会对这个键值对的值进行加密,客户端无法读取到 Cookie 的明文值。默认为 false。 |
在设置 Cookie 时,我们需要考虑这个 Cookie 是否需要被前端获取,失效时间多久等等。
示例:
import { Inject, Controller, Get, Provide } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;
@Get('/')
async home() {
this.ctx.cookies.set('cid', 'hello world', {
domain: 'localhost', // 写cookie所在的域名
path: '/index', // 写cookie所在的路径
maxAge: 10 * 60 * 1000, // cookie有效时长
expires: new Date('2017-02-15'), // cookie失效时间
httpOnly: false, // 是否只用于http请求中获取
overwrite: false, // 是否允许重写
});
ctx.body = 'cookie is ok';
}
}
默认的配置下,Cookie 是加签不加密的,浏览器可以看到明文,js 不能访问,不能被客户端(手工)篡改。
如果想要 Cookie 在浏览器端可以被 js 访问并修改:
ctx.cookies.set(key, value, {
httpOnly: false,
signed: false,
});
如果想要 Cookie 在浏览器端不能被修改,不能看到明文:
ctx.cookies.set(key, value, {
httpOnly: true, // 默认就是 true
encrypt: true, // 加密传输
});
获取 Cookie
使用 ctx.cookies.get(key, options)
API 来获取 Cookie。
由于 HTTP 请求中的 Cookie 是在一个 header 中传输过来的,通过框架提供的这个方法可以快速的从整段 Cookie 中获取对应的键值对的值。上面在设置 Cookie 的时候,我们可以设置 options.signed
和 options.encrypt
来对 Cookie 进行签名或加密,因此对应的在获取 Cookie 的时候也要传相匹配的选项。
- 如果设置的时候指定为 signed,获取时未指定,则不会在获取时对取到的值做验签,导致可能被客户端篡改。
- 如果设置的时候指定为 encrypt,获取时未指定,则无法获取到真实的值 ,而是加密过后的密文。
如果要获取前端或者其他系统设置的 Cookie,需要指定参数 signed
为 false
,避免对它做验签导致获取不到 Cookie 的值。
ctx.cookies.get('frontend-cookie', {
signed: false,
});