You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 10:10:40