使用 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 是一项非常易于使用的服务。但我决定为整个通知服务创建一个单独的章节,以便每个人都能更轻松地理解它