Aller au contenu principal
Version: 2.0.0

Session

Session 在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。

在 @midwayjs/web 使用

框架内置了 Session 插件,给我们提供了 ctx.session 来访问或者修改当前用户 Session 。

比如 Controller 中。

// src/controller/home.ts

import { Controller, Get, Provide, Inject } from '@midwayjs/decorator';
import { Context } from 'egg';

@Provide()
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;

@Get('/')
async home() {
// 获取 Session 上的内容
const userId = this.ctx.session.userId;
// ...

// 修改 Session 的值
this.ctx.session.visited = this.ctx.session.visited ? this.ctx.session.visited + 1 : 1;
}
}

Session 的使用方法非常直观,直接读取它或者修改它就可以了,如果要删除它,直接将它赋值为 null:

ctx.session = null;

需要 特别注意 的是:设置 session 属性时需要避免以下几种情况(会造成字段丢失,详见koa-session源码)

  • 不要以 _ 开头
  • 不能为 isNew
// ❌ 错误的用法
ctx.session._visited = 1; // --> 该字段会在下一次请求时丢失
ctx.session.isNew = 'HeHe'; // --> 为内部关键字, 不应该去更改

// ✔️ 正确的用法
ctx.session.visited = 1; // --> 此处没有问题

Session 的实现是基于 Cookie 的,默认配置下,用户 Session 的内容加密后直接存储在 Cookie 中的一个字段中,用户每次请求我们网站的时候都会带上这个 Cookie,我们在服务端解密后使用。Session 的默认配置如下:

export const session = {
key: 'EGG_SESS',
maxAge: 24 * 3600 * 1000, // 1 天
httpOnly: true,
encrypt: true,
};

可以看到这些参数除了 key 都是 Cookie 的参数,key 代表了存储 Session 的 Cookie 键值对的 key 是什么。在默认的配置下,存放 Session 的 Cookie 将会加密存储、不可被前端 js 访问,这样可以保证用户的 Session 是安全的。

Session 默认存放在 Cookie 中,如需放在其他存储中,具体可以查阅 Cookie 与 Session

在 @midwayjs/koa 使用

koa 原生功能只提供了 Cookie 的操作,但是没有提供 Session 操作。Session 就只用自己实现或者通过第三方中间件实现。

常用的 koa session 中间件有 koa-session。koa-session 默认基于 cookie 来实现 Session 数据存储,同时也支持其他存储的扩展。

首先需要安装中间件。

$ npm install koa-session --save

在启动时加载该中间件,示例如下:

// configuration.ts

import { Configuration, App } from '@midwayjs/decorator';
import { Application } from '@midwayjs/koa';
import * as session from 'koa-session';

@Configuration()
export class AutoConfiguration {
@App()
app: Application;

async onReady() {
this.app.use(
session(
{
key: 'koa.sess', // cookie key
maxAge: 24 * 3600 * 1000, // 1天
},
this.app
)
);
}
}

这里的配置也可以放到 config 中,然后使用 @Config 装饰器注入。

这里中间件的更多选项可以参考 koa-session 文档

之后就可以在其他地方使用了,比如:

// src/controller/home.ts

import { Controller, Get, Provide, Inject } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';

@Provide()
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;

@Get('/')
async home() {
this.ctx.session.visits = this.ctx.session.visits ? this.ctx.session.visits + 1 : 1;
}
}

也可以使用 @Session 装饰器获取 Session。

// src/controller/home.ts

import { Controller, Get, Provide, Inject, Session, ALL } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';

@Provide()
@Controller('/')
export class HomeController {
@Inject()
ctx: Context;

@Get('/')
async home(@Session() visits = 1) {
this.req.session.visits = session.visits = visits + 1;
}
}

或者直接获取整个 Session。

// src/controller/home.ts

import { Controller, Get, Provide, Session, ALL } from '@midwayjs/decorator';

@Provide()
@Controller('/')
export class HomeController {
@Get('/')
async home(@Session(ALL) session) {
session.visits = session.visits ? session.visits + 1 : 1;
}
}

如需使用不同的存储,可以使用类似的 generic-session 来替换该中间件。

在 @midwayjs/express 使用

首先安装依赖。

$ npm i express-session
$ npm i -D @types/express-session

加到中间件中。

// configuration.ts

import { Configuration, App } from '@midwayjs/decorator';
import { Application } from '@midwayjs/express';
import * as session from 'express-session';

@Configuration()
export class AutoConfiguration {
@App()
app: Application;

async onReady() {
this.app.use(
session({
secret: 'my-secret',
resave: false,
saveUninitialized: false,
})
);
}
}

secret 用于对 sessionId cookie 进行加密。它可以是字符串,也可以是多个秘钥字符串组成的数组。如果提供了数组,则使用第一个字符串进行签名,而在验证请求中的签名时会循环所有的加密串。

这个秘钥最好是随机的一组字符。

通过获取 req 对象设置获取 Session 中的值。

// src/controller/home.ts

import { Controller, Get, Provide, Inject } from '@midwayjs/decorator';
import { Context } from '@midwayjs/express';
import { Request } from 'express';

@Provide()
@Controller('/')
export class HomeController {
@Inject()
req: Request;

@Get('/')
async home() {
this.req.session.visits = this.req.session.visits ? this.req.session.visits + 1 : 1;
}
}

也可以使用 @Session 装饰器获取 Session。

// src/controller/home.ts

import { Controller, Get, Provide, Inject, Session, ALL } from '@midwayjs/decorator';
import { Context } from '@midwayjs/express';
import { Request } from 'express';

@Provide()
@Controller('/')
export class HomeController {
@Inject()
req: Request;

@Get('/')
async home(@Session() visits = 1) {
this.req.session.visits = session.visits = visits + 1;
}
}

或者直接获取整个 Session。

// src/controller/home.ts

import { Controller, Get, Provide, Session, ALL } from '@midwayjs/decorator';
import { Context } from '@midwayjs/express';

@Provide()
@Controller('/')
export class HomeController {
@Get('/')
async home(@Session(ALL) session) {
session.visits = session.visits ? session.visits + 1 : 1;
}
}

在 Serverless 场景使用

Serverless 场景有与弹性的存在,没有维护 Session 的必要性,最多只能使用基于 Cookie 的 Session。