8. 守卫:Nest.js 的请求授权工具

8. 守卫:Nest.js 的请求授权工具

介绍

欢迎回来!在前几篇文章中,我们已经了解了如何创建控制器、服务、中间件、管道和异常过滤器,并使用它们来处理 HTTP 请求。在这篇文章中,我们将探讨 Nest.js 中的守卫(Guards)。守卫是处理请求授权的强大工具,可以在请求到达控制器之前检查用户的权限。让我们一起深入了解守卫的工作原理和使用方法。

什么是守卫?

守卫是一个类,它实现了 CanActivate 接口,并包含一个 canActivate 方法。canActivate 方法接收两个参数:执行上下文和当前请求对象。守卫可以用于检查用户的权限,并决定是否允许请求继续进行。

创建一个守卫

让我们通过一个实际例子来了解如何创建和使用守卫。假设我们要创建一个身份验证守卫,用于检查请求是否包含有效的身份验证令牌。

首先,使用 Nest CLI 创建一个新的守卫:

bash 复制代码
nest generate guard auth

这条命令会在 src 目录下生成一个 auth.guard.ts 文件。让我们看看这个文件的内容:

typescript 复制代码
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}
  • @Injectable():将类标记为可注入的服务。
  • canActivate 方法:守卫的核心方法,接收执行上下文和当前请求对象。

使用守卫进行身份验证

让我们修改 AuthGuard,在 canActivate 方法中添加身份验证逻辑。假设我们要检查请求头中是否包含 Authorization 令牌。

修改 auth.guard.ts 文件如下:

typescript 复制代码
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    const authHeader = request.headers.authorization;

    if (!authHeader) {
      throw new UnauthorizedException('Authorization header is missing');
    }

    // 在这里添加令牌验证逻辑
    const token = authHeader.split(' ')[1];
    if (!this.validateToken(token)) {
      throw new UnauthorizedException('Invalid token');
    }

    return true;
  }

  private validateToken(token: string): boolean {
    // 在这里添加令牌验证逻辑
    return token === 'valid-token'; // 示例逻辑
  }
}

在这个例子中,我们检查请求头中是否包含 Authorization 令牌,并验证令牌的有效性。如果令牌无效,我们抛出一个 UnauthorizedException 异常。

应用守卫

要使用守卫,我们需要将其应用到特定的路由或全局范围内。让我们将 AuthGuard 应用到 BooksController 的所有路由。

打开 books.controller.ts 文件,修改如下:

typescript 复制代码
import { Controller, Get, Post, Put, Delete, Param, Body, UseGuards } from '@nestjs/common';
import { BooksService } from './books.service';
import { CreateBookDto } from './create-book.dto';
import { AuthGuard } from './auth.guard';

@Controller('books')
@UseGuards(AuthGuard)
export class BooksController {
  constructor(private readonly booksService: BooksService) {}

  @Get()
  findAll(): string {
    return this.booksService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return this.booksService.findOne(id);
  }

  @Post()
  create(@Body() createBookDto: CreateBookDto): string {
    return this.booksService.create(createBookDto);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateBookDto: CreateBookDto): string {
    return this.booksService.update(id, updateBookDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string): string {
    return this.booksService.remove(id);
  }
}

在这个例子中,我们使用 @UseGuards 装饰器将 AuthGuard 应用到 BooksController。每次请求到达 BooksController 的方法之前,都会先经过 AuthGuard

全局守卫

如果你希望守卫应用于所有路由,可以将其注册为全局守卫。打开 main.ts 文件,修改如下:

typescript 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from './auth.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalGuards(new AuthGuard());
  await app.listen(3000);
}
bootstrap();

在这个例子中,我们使用 app.useGlobalGuards 方法将 AuthGuard 注册为全局守卫。这样,所有的请求都会经过 AuthGuard

角色守卫

有时,我们可能需要根据用户的角色进行授权。让我们创建一个新的守卫,用于检查用户是否具有特定角色。

首先,使用 Nest CLI 创建一个新的守卫:

bash 复制代码
nest generate guard roles

然后,修改 roles.guard.ts 文件如下:

typescript 复制代码
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    const hasRole = () => user.roles.some((role) => roles.includes(role));
    if (!user || !user.roles || !hasRole()) {
      throw new ForbiddenException('You do not have the required roles');
    }

    return true;
  }
}

在这个例子中,我们使用 Reflector 获取路由处理器上的角色元数据,并检查用户是否具有这些角色。

要使用这个角色守卫,我们需要定义一个自定义装饰器,用于将角色元数据添加到路由处理器上。

创建一个新的文件 roles.decorator.ts,内容如下:

typescript 复制代码
import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

然后,修改 books.controller.ts 文件,应用角色守卫和自定义装饰器:

typescript 复制代码
import { Controller, Get, Post, Put, Delete, Param, Body, UseGuards } from '@nestjs/common';
import { BooksService } from './books.service';
import { CreateBookDto } from './create-book.dto';
import { AuthGuard } from './auth.guard';
import { RolesGuard } from './roles.guard';
import { Roles } from './roles.decorator';

@Controller('books')
@UseGuards(AuthGuard, RolesGuard)
export class BooksController {
  constructor(private readonly booksService: BooksService) {}

  @Get()
  @Roles('admin')
  findAll(): string {
    return this.booksService.findAll();
  }

  @Get(':id')
  @Roles('admin', 'user')
  findOne(@Param('id') id: string): string {
    return this.booksService.findOne(id);
  }

  @Post()
  @Roles('admin')
  create(@Body() createBookDto: CreateBookDto): string {
    return this.booksService.create(createBookDto);
  }

  @Put(':id')
  @Roles('admin')
  update(@Param('id') id: string, @Body() updateBookDto: CreateBookDto): string {
    return this.booksService.update(id, updateBookDto);
  }

  @Delete(':id')
  @Roles('admin')
  remove(@Param('id') id: string): string {
    return this.booksService.remove(id);
  }
}

在这个例子中,我们使用 @Roles 装饰器将角色元数据添加到路由处理器上,并使用 @UseGuards 装饰器将 RolesGuard 应用到 BooksController。每次请求到达 BooksController 的方法之前,都会先经过 AuthGuardRolesGuard

结论

在这篇文章中,我们深入探讨了 Nest.js 中的守卫,并通过实际例子展示了如何创建和使用守卫。我们还学习了如何将守卫应用到特定的路由或全局范围内,以及如何根据用户的角色进行授权。

守卫是处理请求授权的强大工具,可以在请求到达控制器之前检查用户的权限。通过使用守卫,我们可以确保只有具有适当权限的用户才能访问特定的资源,使应用更加安全和可靠。

感谢你的阅读!如果你有任何问题或建议,欢迎在评论区留言。我们下次再见!

预告

在下一篇文章中,我们将探讨 Nest.js 中的拦截器(Interceptors)。拦截器是处理请求和响应的强大工具,可以在请求到达控制器之前或响应发送到客户端之前执行一些操作。敬请期待!

相关推荐
热爱编程的小曾22 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin34 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox