上一章,实现了JWT,本节就将JWT用在NestJS项目中。
什么是guard,这里截取NestJS官网的一段话:
A guard is a class annotated with the @Injectable() decorator, which implements the CanActivate interface
Guards have a single responsibility. They determine whether a given request will be handled by the route handler or not, depending on certain conditions (like permissions, roles, ACLs, etc.) present at run-time.
下面是官网提供的一段代码。类中自带一个CanActivate方法,这个方法会返回一个boolean,决定了我们的这个请求是否会继续传递给controller,如果返回false的话,请求就会被阻拦下来。是否将请求放行,传递给controller取决于它是否能通过鉴权。
结合当前的场景,就是判断当前的请求中是否有JWT的token,并通过jwtService判断当前的token是否是有效的
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private readonly jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException();
}
try {
// 💡 Here the JWT secret key that's used for verifying the payload
// is the key that was passed in the JwtModule
const payload = await this.jwtService.verifyAsync(token);
// 💡 We're assigning the payload to the request object here
// so that we can access it in our route handlers
request['user'] = payload;
} catch {
throw new UnauthorizedException();
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
1.创建guard文件
guard同样可以通过Nest-Cli创建,执行命令nest g guard auth/auth --no-spec --flat其中--flat表明不要为这个守卫单独创建一个文件夹,直接把文件扔在 src/auth 下。
然后创建guard文件夹,将auth.guard.ts放在guard文件夹中
2.实现CanActivate逻辑
主要就是从content中提取token信息,然后校验token
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import type { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private readonly jwtService: JwtService) {}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException();
}
try {
const payload = await this.jwtService.verifyAsync(token);
request['user'] = payload;
} catch {
throw new UnauthorizedException();
}
return true;
}
}
3.将guard应用在端点中
在controller对应的方法上面,使用UseGuards的装饰器。
同样的,我们可以全局应用Guards。在module的provides中导入相应配置:
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
APP_GUARD代表全局的guard规则。此时这个guard会作用在全局所有的端点上。
但是全局绑定guard会导致登录和注册,出现问题。因为注册和登录时用户是没有token的。这个问题下一章解决。