使用 Passport 在 NestJS 中实现 JWT 身份验证(第 1 部分)
第一步
`嵌套 g 资源认证`
这将进一步要求您进行选择
`❯ REST API
GraphQL(代码优先)
GraphQL(模式优先)
微服务(非 HTTP)
WebSockets`
选择 REST API,这将为您生成带有 dtos 服务控制器和模块的整个模块
注册用户
由于我们正在实施基于电子邮件/密码的身份验证,因此第一步我们将注册用户。
1 验证传入数据
由于 Nest js 与类验证器等推荐的验证包具有很强的集成性,但根据我以前的经验,我在 React js 前端中使用 Zod 进行大量验证,因此我找到了一个很棒的
Nest js 生态系统的解决方案称为 Nest Zod,所以我现在更愿意使用这个。首先安装库
`npm 和 nestjs-zod`
import { createZodDto } from 'nestjs-zod'; import { z } from 'zod'; const passwordStrengthRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; const registerUserSchema = z .object({ email: z.string().email(), password: z .string() .min(8) .regex( passwordStrengthRegex, 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character', ), confirmPassword: z.string().min(8), }) .refine((data) => data.password === data.confirmPassword, { message: 'Passwords do not match', }); export class RegisterUserDto extends createZodDto(registerUserSchema) {}
然后在路线上应用验证管道
import { Controller, Post, Body, Version, UsePipes } from '@nestjs/common'; import { AuthService } from './auth.service'; import { RegisterUserDto } from './dto/register.dto'; import { ZodValidationPipe } from 'nestjs-zod'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @Version('1') @Post() @UsePipes(ZodValidationPipe) async registerUser(@Body() registerUserDto: RegisterUserDto) { return await this.authService.registerUser(registerUserDto); } }
如果我们提供所有正确的输入

我们已经完成了第一步
让我们净化数据
我们有三个输入
但是我们明确添加了电子邮件:z.string().email(),这对于这种用例来说已经足够了

但为了增加额外的安全性,我们可以添加一个消毒层
import { createZodDto } from 'nestjs-zod'; import { z } from 'zod'; import * as xss from 'xss'; const passwordStrengthRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; const registerUserSchema = z .object({ email: z.string().transform((input) => xss.filterXSS(input)), // Sanitizing input using xss password: z .string() .min(8) .regex( passwordStrengthRegex, 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character', ), confirmPassword: z.string().min(8), }) .refine((data) => data.password === data.confirmPassword, { message: 'Passwords do not match', }); export class RegisterUserDto extends createZodDto(registerUserSchema) {}

这是一个我们再次添加的测试
`电子邮件:z
。细绳()
.电子邮件()`
步骤3,4,5
import { BadRequestException, Injectable, InternalServerErrorException, } from '@nestjs/common'; import { RegisterUserDto } from './dto/register.dto'; import { PrismaService } from 'src/prismaModule/prisma.service'; import * as argon2 from 'argon2'; @Injectable() export class AuthService { constructor(private readonly prismaService: PrismaService) {} async registerUser(registerUserDto: RegisterUserDto) { // data is validate and sanitized by the registerUserDto const { email, password } = registerUserDto; try { // check if user already exists const user = await this.prismaService.user.findFirst({ where: { email, }, }); if (user) { throw new BadRequestException('user already eists '); } //if use not exists lets hash user password const hashedPassword = await argon2.hash(registerUserDto.password); // time to create user const userData = await this.prismaService.user.create({ data: { email, password: hashedPassword, }, }); if (!userData) { throw new InternalServerErrorException( 'some thing went wrong while registring user', ); } // if user is created successfully then send email to user for email varification return { success: true, message: 'user created successfully', }; } catch (error) { throw error; } } }
需要注意的重点是,我刚刚返回了一条成功消息,但没有相关数据
给用户 ID 或电子邮件,因为它不需要在此步骤中将数据发送回用户。注册后,用户将被重定向到登录页面以填写详细信息,因此避免发送不必要的数据是一种很好的安全做法

速率限制
在 nestjs 中实现速率限制非常容易,只需安装 nestjs/throttler 并进行全局配置即可。
要安装软件包,请运行“npm i --save @nestjs/throttler”
@Module({ imports: [ ThrottlerModule.forRoot([{ ttl: 60000, limit: 10, }]), ], }) export class AppModule {}
然后将 nestjs throttle guard 添加为全局守卫
providers: [ AppService, { provide: APP_GUARD, useClass: ThrottlerGuard, }, ],
下面是
import { Controller, Post, Body, Version, UsePipes, Req } from '@nestjs/common'; import { AuthService } from './auth.service'; import { RegisterUserDto } from './dto/register.dto'; import { ZodValidationPipe } from 'nestjs-zod'; import { Throttle } from '@nestjs/throttler'; @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @Throttle({ default: { ttl: 100000, // 1 minute limit: 5, // 5 requests per minute }, }) @Version('1') @Post() @UsePipes(ZodValidationPipe) async registerUser(@Body() registerUserDto: RegisterUserDto, @Req() req) { return await this.authService.registerUser(registerUserDto, req); } }
由于注册用户端点是一个敏感端点,因此暴力破解
否则可能会发生字典攻击,因此我们严格限制速率
发送验证邮件
用于向用户发送验证电子邮件,Resend 是一项非常易于使用的服务。但我决定为整个通知服务创建一个单独的章节,以便每个人都能更轻松地理解它