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

使用bcrypt修改哈希密码后Node.js用户无法登录问题排查

问题:修改密码后无法使用新密码登录

在Node.js应用中通过Mongoose的userSchema实现用户登录及CRUD操作,创建、登录、登出功能正常,但修改密码后无法用新密码登录,登录提示“incorrect password”。

相关代码

用户Schema定义

const userSchema = new mongoose.Schema({
    username: {
        type: String,
        unique: true,
        required: [true, "Please enter a username"],
        trim: true,
        minlength: [3, "Minimum username length is 3 characters"],
        maxlength: [10, "Maximum username length is 10 characters"]
    },
    password: {
        type: String,
        trim: true,
        required: [true, "Must provide a password"],
        minlength: [6, "Minimum password length is 6 characters"]
    },
    email:{
        type: String,
        unique: true,
        required: [true, "Please enter an email"],
        validate: [isEmail, "Please enter a valid email"]
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

userSchema.pre("save", async function(next) {
    if (this.isModified("password")) {
        const salt = await bcrypt.genSalt();
        this.password = await bcrypt.hash(this.password, salt);
    }
    next();
});
userSchema.statics.login = async function(username, password) {
    const user = await this.findOne({ username });
    if (user) {
        const auth = await bcrypt.compare(password, user.password);
        if (auth) {
            return user;
        }
        throw Error("incorrect password");
    }
    throw Error("incorrect username");
});

登录与修改密码接口

const login_post = async (req, res) => {
    const { username, password } = req.body;
    try {
        const user = await User.login(username, password);
        const token = createToken(user._id);
        res.cookie("jwt", token, {
            httpOnly: true,
            sessionLength: sessionLength * 1000
        });
        res.status(201).json({ user: user._id, message: token });
    } catch (error) {
        const errors = handleErrors(error);
        res.status(400).json({ errors });
    }
};

const changePassword = async (req, res) => {
    const { oldPassword, newPassword, confirmPassword } = req.body;
    try {
        const userId = req.user.id;
        const user = await User.findById(userId);

        if (!user) {
            return res.status(404).json({ message: "User not found" });
        }
        if (newPassword !== confirmPassword) {
            return res.status(400).json({ message: "Passwords do not match" });
        }
        const auth = await bcrypt.compare(oldPassword, user.password);
        if (!auth) {
            return res.status(400).json({ message: "Incorrect old password" });
        }
        const salt = await bcrypt.genSalt();
        const hashedNewPassword = await bcrypt.hash(newPassword, salt);
        user.password = hashedNewPassword;
        await user.save();
        res.status(200).json({ message: "Password updated successfully" });
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
};

问题原因

问题出在密码哈希的重复执行:

  • 在changePassword接口中手动对新密码进行了哈希处理,得到hashedNewPassword后赋值给user.password
  • 调用user.save()时,触发了Schema上的pre("save")中间件,中间件检测到password字段被修改,会再次对已经哈希过的密码进行二次哈希
  • 最终数据库中存储的是两次哈希后的密码,登录时用原新密码和这个二次哈希值对比,自然不匹配

解决方案

去掉changePassword接口中手动哈希密码的代码,直接将明文新密码赋值给user.password,让pre("save")中间件统一处理哈希逻辑即可:

修改后的changePassword函数:

const changePassword = async (req, res) => {
    const { oldPassword, newPassword, confirmPassword } = req.body;
    try {
        const userId = req.user.id;
        const user = await User.findById(userId);

        if (!user) {
            return res.status(404).json({ message: "User not found" });
        }
        if (newPassword !== confirmPassword) {
            return res.status(400).json({ message: "Passwords do not match" });
        }
        const auth = await bcrypt.compare(oldPassword, user.password);
        if (!auth) {
            return res.status(400).json({ message: "Incorrect old password" });
        }
        // 直接赋值明文密码,交给pre save中间件处理哈希
        user.password = newPassword;
        await user.save();
        res.status(200).json({ message: "Password updated successfully" });
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
};

这样修改后,新密码只会被哈希一次,存储到数据库中的哈希值和登录时的对比逻辑一致,就能正常登录了。

内容的提问来源于stack exchange,提问作者kerolos aziz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 03:05:10