Node.js中Sequelize哈希密码前实现自定义验证的问题
问题根源
你当前的逻辑错误在于先哈希密码再传入模型创建,导致模型的validatePassword验证的是哈希后的字符串,而非用户输入的原始密码——哈希后的字符串显然不符合你定义的密码格式规则,所以验证完全失效。
解决方案
方案一:控制器提前验证(快速修复)
把密码格式验证逻辑放在哈希操作之前,验证通过再执行哈希:
const createUser = async (req, res) => { try { const { first_name, last_name, email, password, mobile_number, role } = req.body; // 先验证原始密码格式 const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,12}$/; if (!passwordRegex.test(password)) { return res.status(HttpStatus.BAD_REQUEST.code).json({ success: false, msg: 'The password must contain at least 8 and maximum 12 characters including at least 1 uppercase, 1 lowercase, one number and one special character.' }); } // 验证通过后哈希密码 const salt = await bcrypt.genSalt(); const hashPassword = await bcrypt.hash(password, salt); const user = await User.create({ first_name, last_name, email, mobile_number, password: hashPassword, role: role || 'user' }); res.status(HttpStatus.CREATED.code).send({ msg: `USER ${HttpStatus.CREATED.msg}`, user, }); } catch (error) { if (error.name === 'SequelizeValidationError') { return res.status(HttpStatus.BAD_REQUEST.code).json({ success: false, msg: error.errors.map(e => e.message) }); } else { return res.status(HttpStatus.INTERNAL_SERVER_ERROR.code).json({ success: false, msg: error.message }); } } };
同时可以移除模型中password字段的validatePassword验证,避免重复校验。
方案二:Sequelize钩子(推荐,符合MVC架构)
把密码哈希逻辑移到模型钩子中,确保先验证原始密码,再自动哈希后存入数据库:
模型代码修改
const { DataTypes } = require("sequelize"); const sequelize = require("../helper/db.config"); const bcrypt = require('bcrypt'); // 引入bcrypt const User = sequelize.define("User", { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false, }, first_name: { type: DataTypes.STRING, allowNull: false, validate: { notNull: { msg: 'Please enter your first name' } } }, last_name: { type: DataTypes.STRING, allowNull: false, validate: { notNull: { msg: 'Please enter your last name' } } }, email: { type: DataTypes.STRING, unique: true, allowNull: false, validate: { isEmail: { args: true, msg: 'email format is not correct' }, notNull: { args: true, msg: 'email can\'t be empty' }, notEmpty: { args: true, msg: 'email can\'t be empty string' }, } }, password: { type: DataTypes.STRING, allowNull: false, validate: { validatePassword: function(password) { if(!(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,12}$/.test(password))) { throw new Error('The password must contain at least 8 and maximum 12 characters including at least 1 uppercase, 1 lowercase, one number and one special character.'); } } }, }, mobile_number: { type: DataTypes.STRING, allowNull: false, validate: { notNull: { msg: 'Please enter your mobile number' } } }, role: { type: DataTypes.ENUM, values: ['user', 'admin', 'teacher', 'staff'], defaultValue: 'user', }, }); // 钩子:验证通过后自动哈希密码 User.beforeCreate(async (user) => { const salt = await bcrypt.genSalt(); user.password = await bcrypt.hash(user.password, salt); }); module.exports = User;
控制器代码修改
不再手动哈希,直接传入原始密码:
const createUser = async (req, res) => { try { const { first_name, last_name, email, password, mobile_number, role } = req.body; const user = await User.create({ first_name, last_name, email, mobile_number, password, // 直接传原始密码 role: role || 'user' }); res.status(HttpStatus.CREATED.code).send({ msg: `USER ${HttpStatus.CREATED.msg}`, user, }); } catch (error) { if (error.name === 'SequelizeValidationError') { return res.status(HttpStatus.BAD_REQUEST.code).json({ success: false, msg: error.errors.map(e => e.message) }); } else { return res.status(HttpStatus.INTERNAL_SERVER_ERROR.code).json({ success: false, msg: error.message }); } } };
内容的提问来源于stack exchange,提问作者Zalan khan
相关产品推荐
相关产品推荐

