Node.js中bcryptjs compare校验MongoDB密码始终返回false
bcrypt.compare始终返回false问题排查
核心问题原因
- Schema配置错误:
password字段设置了lowercase: true属性,Mongoose写入数据时会自动将字段值转为全小写。而bcrypt生成的哈希值是大小写敏感的,包含大小写混合字符,强制转小写会直接破坏哈希结构,导致比对永远失败。 - 注册接口逻辑顺序错误:先调用
User.create将前端传入的明文密码直接存入数据库,之后才执行bcrypt哈希操作,且后续newUser.save()没有加await,存在时序问题,数据库中存储的根本不是正确生成的哈希值。 - 登录接口写法不规范:重复查询用户数据、同时混用bcrypt的Promise调用和回调写法、catch块为空不打印错误,既容易产生逻辑bug,也会掩盖实际运行报错。
分步修复方案
1. 修正User Schema配置
删除password字段的lowercase: true配置,避免哈希值被自动转写:
const userSchema = new mongoose.Schema({ email: { type: String, required: [true, "Please enter a email"], unique: true, lowercase: true, }, password: { type: String, required: [true, "Please enter a password"], // 移除lowercase: true配置,禁止自动转换哈希值大小写 }, });
2. 修正注册接口逻辑
调整执行顺序:先对明文密码做哈希处理,再将哈希值写入数据库,所有异步操作必须加await确保执行完成:
module.exports.signupPost = async (req, res) => { const { email, password } = req.body; try { // 先生成密码哈希 const hashedPassword = await bcrypt.hash(password, 12); // 存储时直接写入哈希值 const newUser = await User.create({ email, password: hashedPassword }); res.status(200).json({ user: newUser._id }); } catch (err) { const errors = handlerErr(err); res.status(400).json({ errors }); } };
3. 修正登录接口逻辑
删除重复的用户查询代码,统一使用bcrypt的Promise写法,补全异常处理逻辑:
module.exports.loginPost = async (req, res) => { const { email, password } = req.body; try { const user = await User.findOne({ email }); if (!user) { return res.status(404).json({ email: "No user found" }); } // 密码比对,不要同时传回调函数和使用await接收结果 const isMatch = await bcrypt.compare(password, user.password); if (isMatch) { // 此处编写登录成功后的业务逻辑,比如生成token、返回用户信息 res.status(200).json({ msg: "Login success" }); } else { return res.status(400).json({ password: "Password is incorrect" }); } } catch (err) { console.log("Login error: ", err); res.status(500).json({ error: "Internal server error" }); } };
修复完成后必须删除之前注册的旧测试用户,重新走注册流程生成新数据。旧数据存储的要么是明文密码、要么是被转成小写的无效哈希,永远无法比对通过。
内容的提问来源于stack exchange,提问作者Dev Dub
相关产品推荐
相关产品推荐

