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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 23:53:15