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)。拦截器是处理请求和响应的强大工具,可以在请求到达控制器之前或响应发送到客户端之前执行一些操作。敬请期待!

相关推荐
xiyusec18 分钟前
HTML基础
前端·html
好开心331 小时前
javaScript交互案例2
开发语言·前端·javascript·html·ecmascript·交互
xChive1 小时前
优化表单交互:在 el-select 组件中嵌入表格显示选项
前端·vue.js·交互·element-plus
tian-ming1 小时前
(十八)JavaWeb后端开发案例——会话/yml/过滤器/拦截器
java·开发语言·前端
_jacobfu1 小时前
mac2024 安装node和vue
前端·javascript·vue.js
Ztiddler1 小时前
【npm设置代理-解决npm网络连接error network失败问题】
前端·后端·npm·node.js·vue
三天不学习1 小时前
前端工程化-node/npm/babel/polyfill/webpack 一文速通
前端·webpack·npm
羽羽Ci Ci1 小时前
axios vue.js
前端·javascript·vue.js
岳哥i2 小时前
前端项目接入单元测试手册
前端·单元测试
小彭努力中2 小时前
138. CSS3DRenderer渲染HTML标签
前端·深度学习·3d·webgl·three.js