3-6:Guard

0 阅读2分钟

上一章,实现了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。在moduleprovides中导入相应配置:

providers: [
  {
    provide: APP_GUARD,
    useClass: AuthGuard,
  },
],

APP_GUARD代表全局的guard规则。此时这个guard会作用在全局所有的端点上。

image.png

但是全局绑定guard会导致登录和注册,出现问题。因为注册和登录时用户是没有token的。这个问题下一章解决。