上节我们知道了保存登录状态的两种方式,session + cookie、jwt,这节我们用 Nest 来实现下吧。
首先用 @nest/cli 快速创建一个 Nest.js 项目
nest new jwt-and-session -p npm
我们先实现 session + cookie 的方式:
Nest 里实现 session 实现还是用的 express 的中间件 express-session。
安装 express-session 和它的 ts 类型定义:
npm install express-session @types/express-session
然后在入口模块里启用它:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as session from 'express-session';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(session({
secret: 'guang',
resave: false,
saveUninitialized: false
}));
await app.listen(3000);
}
bootstrap();
使用 express-session 中间件,指定加密的密钥 secret。
resave 为 true 是每次访问都会更新 session,不管有没有修改 session 的内容,而 false 是只有 session 内容变了才会去更新 session。
saveUninitalized 设置为 true 是不管是否设置 session,都会初始化一个空的 session 对象。比如你没有登录的时候,也会初始化一个 session 对象,这个设置为 false 就好。
然后在 controller 里就可以注入 session 对象:
@Get('sss')
sss(@Session() session) {
console.log(session)
session.count = session.count ? session.count + 1 : 1;
return session.count;
}
我在 session 里放了个 count 的变量,每次访问加一,然后返回这个 count。
这样就可以判断 http 请求是否有了状态。
把它跑起来:
nest start --watch
然后用 postman 测试下:
可以看到每次请求返回的数据都不同,而且返回了一个 cookie 是 connect.sid,这个就是对应 session 的 id。
因为 cookie 在请求的时候会自动带上,就可以实现请求的标识,给 http 请求加上状态。
session + cookie 的方式用起来还是很简单的,我们再来看下 jwt 的方式:
jwt 需要引入 @nestjs/jwt 这个包
npm install @nestjs/jwt
然后在 AppModule 里引入 JwtModule:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
JwtModule.register({
secret: 'guang',
signOptions: {
expiresIn: '7d'
}
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
JwtModule 是一个动态模块,通过 register 传入 option。
或者是 registerAsync,然后通过 useFactory 异步拿到 option 传入:
这部分是动态模块的知识,忘了的同学可以看看动态模块那节。
指定 secret,也就是加密 jwt 的密钥,还有 token 过期时间 expiresIn,设置 7 天。
然后在 controller 里注入 JwtModule 里的 JwtService:
然后添加一个 handler:
@Get('ttt')
ttt(@Res({ passthrough: true}) response: Response) {
const newToken = this.jwtService.sign({
count: 1
});
response.setHeader('token', newToken);
return 'hello';
}
这里使用 jwtService.sign 来生成一个 jwt token,放到 response header 里。
因为注入 response 对象之后,默认不会把返回值作为 body 了,需要设置 passthrough 为 true 才可以。
然后访问下试试看:
可以看到,返回的响应确实带上了这个 header。
后面的请求需要带上这个 token,在服务端取出来,然后 +1 之后再放回去:
@Get('ttt')
ttt(@Headers('authorization') authorization: string, @Res({ passthrough: true}) response: Response) {
if(authorization) {
try {
const token = authorization.split(' ')[1];
const data = this.jwtService.verify(token);
const newToken = this.jwtService.sign({
count: data.count + 1
});
response.setHeader('token', newToken);
return data.count + 1
} catch(e) {
console.log(e);
throw new UnauthorizedException();
}
} else {
const newToken = this.jwtService.sign({
count: 1
});
response.setHeader('token', newToken);
return 1;
}
}
通过 @Headers 装饰器取出 autorization 的 header,然后通过 jwtService.verify 对它做验证。
如果验证失败,那就抛出 UnauthorizedException 异常,让 Nest 内置的 Exception Filter 来处理。
验证成功就重新生成 jwt 放到 header 里返回。
如果没有 autorization 的 header,那就生成一个 jwt 放到 header 里返回。
然后我们测试下。
第一次访问,会返回 jwt token,把它复制下来:
放到请求的 header 里:
这时候响应为 2,并且返回一个新的 token:
把它复制下来放到 header 里再次请求:
这时候返回的就是 3 了。
这就是通过 jwt 保存状态的方式。
那我们带一个错误的 token 呢?
这时候 jwtService.verify 方法就会抛异常,然后我们返回了 401 错误。
这样,我们就分别用 Nest 分别实现了 session + cookie 和 jwt 两种保存 http 状态的方式。
携带 jwt 需要加载 authorization 的 header 里,以 Bearer xxx 的格式,但是返回 jwt 可以放在任何地方,header、cookie 或者 body 里都可以。
案例代码在小册仓库。
总结
我们分别在 nest 里实现了 session、jwt 两种给 http 添加状态的方式。
session 使用的是 express 的 express-session 中间件,通过 @Session 装饰器取出来传入 controller 里。
jwt 需要引入 @nestjs/jwt 包的 JwtModule,注入其中的 JwtService,然后通过 jwtService.sign 生成 token,通过 jwtService.verify 验证 token。
token 放在 authorization 的 header 里。
session 或者 jwt 都是非常常用的给 http 添加状态的方式,下节我们用这两种方式实现下登录注册功能。