使用bcrypt哈希密码时Mongoose密码验证失效问题求助
问题描述
使用bcrypt库对密码哈希时,Mongoose Schema中定义的密码必填验证规则失效——即使不提供密码也能成功提交;但如果将password: hashedPassword替换为password: req.body.password,验证规则又能正常生效。如何在实现密码哈希的同时保证验证规则有效?
现有代码
控制器代码
import { User } from "../model/userModel.js"; import bcrypt from 'bcrypt'; export const addUser = async (req, res) => { try { const salt = await bcrypt.genSalt(); const hashedPassword = await bcrypt.hash(req.body.password, salt); const addNewUser = new User({ username: req.body.username, email: req.body.email, password: hashedPassword }) await addNewUser.save(); return res.status(200).json({ "res": 'User has been registered successfully' }); } catch (err) { return res.status(500).json({ "res": `${err} Error occurred while signing up the user` }); }; };
用户Schema
const userSchema = new mongoose.Schema({ username: { type: String, required: [true, 'Username is required'], minlength: [2, 'Username required minimum 2 char'], maxlength: [20, 'username can not contain more than 20 char'], trim: true }, email: { type: String, required: [true, 'Email is required'], unique: [true, 'Email is already registered in server'], trim: true }, password: { type: String, required: [true, 'Password is required'], trim: true, minlength: [6, 'Password can not be less than 6 char'], } });
模型代码
export const User = mongoose.model('userspace', userSchema);
问题原因
当req.body.password不存在(即用户未提交密码)时,bcrypt.hash()会把undefined转换为字符串"undefined",再进行哈希操作,最终生成一个符合字符串类型的哈希值。此时Mongoose检查password字段时,发现它有有效值,就会跳过required验证规则。
解决方案
推荐两种可行方案,优先选择第二种(更符合Mongoose的设计理念):
方案一:控制器层提前校验
在执行哈希操作前,先检查req.body.password是否存在且符合要求,不符合则直接返回错误:
export const addUser = async (req, res) => { try { // 提前校验密码 if (!req.body.password || req.body.password.trim().length < 6) { return res.status(400).json({ "res": 'Password is required and must be at least 6 characters' }); } const salt = await bcrypt.genSalt(); const hashedPassword = await bcrypt.hash(req.body.password.trim(), salt); const addNewUser = new User({ username: req.body.username, email: req.body.email, password: hashedPassword }) await addNewUser.save(); return res.status(200).json({ "res": 'User has been registered successfully' }); } catch (err) { return res.status(500).json({ "res": `${err} Error occurred while signing up the user` }); }; };
方案二:使用Mongoose pre-save中间件(推荐)
将密码哈希逻辑移到Schema的pre('save')中间件中,这样Mongoose会先执行字段验证,验证通过后再进行哈希操作:
修改后的用户Schema
const userSchema = new mongoose.Schema({ username: { type: String, required: [true, 'Username is required'], minlength: [2, 'Username required minimum 2 char'], maxlength: [20, 'username can not contain more than 20 char'], trim: true }, email: { type: String, required: [true, 'Email is required'], unique: [true, 'Email is already registered in server'], trim: true }, password: { type: String, required: [true, 'Password is required'], trim: true, minlength: [6, 'Password can not be less than 6 char'], } }); // 添加pre-save中间件处理密码哈希 userSchema.pre('save', async function(next) { // 仅当密码字段被修改(或新增)时才执行哈希 if (!this.isModified('password')) return next(); const salt = await bcrypt.genSalt(); this.password = await bcrypt.hash(this.password, salt); next(); });
修改后的控制器代码
import { User } from "../model/userModel.js"; export const addUser = async (req, res) => { try { const addNewUser = new User({ username: req.body.username, email: req.body.email, password: req.body.password }) await addNewUser.save(); return res.status(200).json({ "res": 'User has been registered successfully' }); } catch (err) { return res.status(500).json({ "res": `${err} Error occurred while signing up the user` }); }; };
这种方式的优势在于:
- 验证规则优先执行,确保密码符合要求后再哈希
- 哈希逻辑与控制器解耦,复用性更强
- 自动处理密码修改场景(仅当密码变化时才重新哈希)
内容的提问来源于stack exchange,提问作者Rakesh1999
相关产品推荐
相关产品推荐

