NestJS结合MySQL使用bcrypt实现登录密码比对问题求助
NestJS登录接口bcrypt密码比对解决方案
问题说明
在NestJS登录接口中,已通过邮箱查询到对应用户,但不知道如何获取数据库中的哈希密码完成bcrypt比对,且未使用TypeORM。
修正后的AuthService代码
import { ConflictException, Injectable, UnauthorizedException } from '@nestjs/common'; import { SignInDto, SignUpDto } from '../dto'; import { execute } from '../mysql'; import * as bcrypt from 'bcrypt'; @Injectable() export class AuthService { // ------SignUp------- public async CREATE(Dto: SignUpDto): Promise<any> { const [account]: any = await execute( ` SELECT * FROM account WHERE email = ? AND is_active = ? AND is_deleted = ? `, [Dto.email.toLowerCase(), 1, 0], ); if (account) { throw new ConflictException('Account already exists on this email id.'); } // 先验证两次密码输入一致,无需哈希confirmPassword if (Dto.password !== Dto.confirmPassword) { throw new ConflictException('Password and confirm password do not match.'); } Dto.email = Dto.email.toLowerCase(); // 仅哈希需要存储的password,存储到数据库 Dto.password = await bcrypt.hash(Dto.password, 12); // 移除confirmPassword,避免存入数据库 const { confirmPassword, ...data } = Dto; return await execute(`INSERT INTO account SET ?`, [data]); } // -------SignIn--------- public async GET(Dto: SignInDto): Promise<any> { const [isExist]: any = await execute( ` SELECT * FROM account WHERE email = ? AND is_active = ? AND is_deleted = ? `, [Dto.email.toLowerCase(), 1, 0], ); if (!isExist) { throw new ConflictException('Account does not exist.'); } // 比对用户输入的明文密码和数据库中的哈希密码 const isPasswordValid = await bcrypt.compare(Dto.password, isExist.password); if (!isPasswordValid) { throw new UnauthorizedException('Invalid password.'); } return { id: isExist.id, email: isExist.email }; } }
关键修改点
- 注册逻辑优化:
- 先验证
password和confirmPassword是否一致,不匹配直接抛出异常 - 仅哈希需要存储的
password,无需哈希confirmPassword,且移除该字段避免存入数据库
- 先验证
- 登录密码比对逻辑:
- 查询到用户后,调用
bcrypt.compare(明文密码, 数据库哈希密码)验证密码有效性 - 密码不匹配时抛出
UnauthorizedException(更符合HTTP语义的异常类型) - 调整异常抛出的位置,确保逻辑顺序正确
- 查询到用户后,调用
修正后的AuthController代码(补充异常处理)
import { Controller, Post, Body, HttpCode, HttpStatus, Res } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { SignUpDto, SignInDto } from '../dto'; import { Response } from 'express'; import { AuthService } from './auth.service'; @Controller('auth') @ApiTags('Auth') export class AuthController { constructor(private readonly _authService: AuthService) { } @Post('/sign-up') @HttpCode(HttpStatus.OK) @ApiResponse({ status: HttpStatus.OK, description: 'Registration completed successfully.' }) @ApiResponse({ status: HttpStatus.CONFLICT, description: 'Account exists or password mismatch.' }) @ApiOperation({ summary: 'User SignUp' }) public async SIGNUP(@Res() res: Response, @Body() Dto: SignUpDto): Promise<any> { try { await this._authService.CREATE(Dto); return res.status(HttpStatus.OK).json({ status: HttpStatus.OK, message: `Registration completed successfully.` }); } catch (error) { return res.status(error.getStatus()).json({ status: error.getStatus(), message: error.message }); } } @Post('/sign-in') @HttpCode(HttpStatus.OK) @ApiResponse({ status: HttpStatus.OK, description: 'Login successful.' }) @ApiResponse({ status: HttpStatus.CONFLICT, description: 'Account does not exist.' }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Invalid password.' }) @ApiOperation({ summary: 'User SignIn' }) public async SIGNIN(@Res() res: Response, @Body() Dto: SignInDto): Promise<any> { try { const result = await this._authService.GET(Dto); return res.status(HttpStatus.OK).json({ status: HttpStatus.OK, data: result, message: `Login successful` }); } catch (error) { return res.status(error.getStatus()).json({ status: error.getStatus(), message: error.message }); } } }
补充说明
- 使用
UnauthorizedException替代ConflictException处理密码错误更符合RESTful规范,对应HTTP 401状态码 - 注册时不要存储
confirmPassword,仅用它验证两次输入一致即可 bcrypt.compare会自动处理哈希的盐值验证,无需手动提取盐值
内容的提问来源于stack exchange,提问作者demi
相关产品推荐
相关产品推荐

