第一步
nest g 资源授权
这将进一步要求您进行选择
❯ REST API
GraphQL(代码优先)
GraphQL(模式优先)
微服务(非 HTTP)
WebSocket
选择 REST API,这将为您生成带有 dtos 服务控制器和模块的整个模块
注册用户
由于我们实施基于电子邮件/密码的身份验证作为第一步,我们将注册用户。
- 首先验证以确保数据合法,并添加密码强度验证以减轻暴力攻击。
- 之后进行消毒,以确保数据可以安全使用。
- 检查数据库中是否已存在该用户记录,如果存在,则表示该用户已经拥有帐户,因此请发送此电子邮件已注册的回复。
- 如果上述检查失败,则意味着我们需要注册一个用户
获取用户密码并使用良好的哈希库(如 bcrypt 或 argon2
)对其进行哈希处理
- 散列后将用户记录插入数据库。
- 向用户发送电子邮件以验证电子邮件是否合法。
- 为路由添加速率限制以避免 DDoS 攻击
1 验证传入数据
由于 Nest js 与类验证器等推荐的验证包有很强的集成,但根据我之前的经验,我在 React JS 前端使用 zod 进行验证,所以我发现了一个很棒的
Nest js 生态系统的解决方案称为 Nests Zod,所以我现在更喜欢使用这个。首先安装库
npm 我 Nestjs-zod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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) {}
|
登录后复制
登录后复制
然后在路线上应用验证管道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(),这对于这个用例来说已经足够了

但是为了增加额外的安全性,我们可以添加一个消毒层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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)),
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
.string()
.email()
步骤3,4,5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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) {}
|
登录后复制
登录后复制
需要注意的要点我刚刚返回了一条成功消息,没有相关数据
像 ID 或电子邮件一样向用户发送,因为在此步骤中不需要向用户发送回数据。注册后,用户将被重定向到登录页面以填写详细信息,因此避免发送不必要的数据是一个很好的安全实践

速率限制
在nestjs中实现速率限制非常简单,只需安装nestjs/throttler并进行全局配置即可。
要安装包,请运行 npm i --save @nestjs/throttler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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);
}
}
|
登录后复制
登录后复制
然后添加nestjsthrottleguard作为全局守卫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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)),
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) {}
|
登录后复制
登录后复制
就是这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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) {
const { email, password } = registerUserDto;
try {
const user = await this.prismaService.user.findFirst({
where: {
email,
},
});
if (user) {
throw new BadRequestException( 'user already eists ' );
}
const hashedPassword = await argon2.hash(registerUserDto.password);
const userData = await this.prismaService.user.create({
data: {
email,
password: hashedPassword,
},
});
if (!userData) {
throw new InternalServerErrorException(
'some thing went wrong while registring user' ,
);
}
return {
success: true,
message: 'user created successfully' ,
};
} catch (error) {
throw error;
}
}
}
|
登录后复制
因为注册用户端点是一个敏感端点暴力破解
否则可能会发生字典攻击,我们严格限制速率
发送验证邮件
用于向用户发送验证电子邮件的使用 Resend 是一项非常棒的易于使用的服务。但我决定为整个通知服务创建一个单独的剧集,以便每个人都更容易理解它
以上是在 NestJS 中使用 Passport 实现 JWT 身份验证日(第 1 部分)的详细内容。更多信息请关注PHP中文网其他相关文章!