Skip to content

守卫

作用

比如说 有些接口只有管理员才能访问,普通用户没有权限访问,那么这个时候就可以使用守卫来控制权限

守卫只接受 true 或者 false,如果返回 true 则继续执行,如果返回 false 则抛出异常

基础版

创建自定义守卫

ts
import {
  CanActivate,
  ExecutionContext,
  HttpException,
  Injectable,
} from "@nestjs/common";
import { Observable } from "rxjs";

@Injectable()
export class GlobalGuard implements CanActivate {
  canActivate(
    context: ExecutionContext
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    // 白名单
    const whiteList = ["/logins", "/logins/test"];
    // 验证
    if (whiteList.includes(request.url)) {
      return true;
    } else {
      throw new HttpException("没有权限", 403);
    }
  }
}

全局挂载守卫

ts
import { Module } from "@nestjs/common";
import { LoginModule } from "./Login/login.module";
import { GlobalGuard } from "./Global/Gurds/global.guard";
import { GlobalExceptionFilter } from "./Global/filter/global.filter";
import { APP_FILTER, APP_GUARD } from "@nestjs/core";
@Module({
  imports: [LoginModule],
  controllers: [],
  providers: [
    {
      provide: APP_GUARD,
      useClass: GlobalGuard,
    },
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionFilter,
    },
  ],
})
export class AppModule {}

局部守卫

  • 新建一个守卫
ts
import {
  CanActivate,
  ExecutionContext,
  HttpException,
  Injectable,
} from "@nestjs/common";
import { Observable, of } from "rxjs";

@Injectable()
export class CustomGuard implements CanActivate {
  canActivate(
    context: ExecutionContext
  ): boolean | Promise<boolean> | Observable<boolean> {
    const req = context.switchToHttp().getRequest();
    if (req.headers["authorization"] == "123456") {
      return true;
    } else {
      // throw new HttpException('没权限666', 403);
      return false;
    }
  }
}
  • 在控制器中使用守卫
ts
import { CustomGuard } from '../../Global/Gurds/custom.guard';
import {
  Controller,
  Get,
  Inject,
  UseGuards,
} from '@nestjs/common';
// 使用守卫
@Get('test2')
@UseGuards(CustomGuard)
findAlltest2() {
  return this.loginService.findAll();
}

警告

  • 如果全局守卫和局部守卫同时存在,那么先执行全局守卫,再执行局部守卫
  • 如果 return false 它也相当于抛出异常,只不过没有传递 message 它找默认的

进阶版

授权

  • (1) 先通过 token 获取用户信息,然后判断用户是否具有权限,如果具有权限则添加权限,否则就不添加
ts
import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
import { Observable } from "rxjs";

@Injectable()
export class SetGuard implements CanActivate {
  canActivate(
    context: ExecutionContext
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    if (request.headers["authorization"] == "123456") {
      request.roles = ["admin", "user"];
    }
    return true;
  }
}
  • (2) 自定义装饰器
ts
import { SetMetadata } from "@nestjs/common";

export const Role = (args: string[]) => SetMetadata("roles", args);

注意

roles 名字可以换 对应下面的自定义装饰器 reflector 里面的名字

  • (3) 把授权装饰器绑定到全局 ,路由上面有自定义装饰器,那么就会执行守卫
  1. 绑定到全局
ts
import { Module } from "@nestjs/common";
import { LoginModule } from "./Login/login.module";
import { SetGuard } from "./Global/Gurds/set.guard";
import { GlobalExceptionFilter } from "./Global/filter/global.filter";
import { APP_FILTER, APP_GUARD } from "@nestjs/core";
@Module({
  imports: [LoginModule],
  controllers: [],
  providers: [
    {
      provide: APP_GUARD,
      useClass: SetGuard,
    },
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionFilter,
    },
  ],
})
export class AppModule {}
  1. 路由控制器使用自定义装饰器
ts
  @Get('test')
  @Role(['admin', 'user'])
  async findAlltest() {
    return this.loginService.findAll();
  }

鉴权

  1. 创建获取权限的守卫
ts
import {
  CanActivate,
  ExecutionContext,
  Inject,
  Injectable,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { Reflector } from "@nestjs/core";
@Injectable()
export class GetGuard implements CanActivate {
  @Inject()
  private reflector: Reflector;

  canActivate(
    context: ExecutionContext
  ): boolean | Promise<boolean> | Observable<boolean> {
    // 获取到规则器中定义的roles
    const roles = this.reflector.get<string[]>("roles", context.getHandler());
    if (!roles) {
      return true;
    } else {
      let ueserRole = context.switchToHttp().getRequest().roles;
      return roles.some((item) => {
        return ueserRole.includes(item);
      });
    }
  }
}
  1. 绑定到获取权限的路由
ts
@Get('test')
@Role(['admin', 'user'])
@UseGuards(GetGuard)
async findAlltest() {
  console.log('孔明');
  return this.loginService.findAll();
}