bcrypt.compare对比密码始终返回false的问题求助
bcrypt.compare始终返回false,登录验证失败问题排查
我遇到了bcrypt.compare突然失效的问题,调用后doMatch始终返回false。目前可以正常创建带哈希密码的新用户,但无法完成登录。
登录控制器代码
const postLoginController = async (req, res) => { const email = req.body.email; const password = req.body.password; try { User.findOne({ email: email }).then((user) => { console.log('password, user.password', password, user.password); bcrypt.compare(password, user.password).then((doMatch) => { console.log('doMatch', doMatch); //Comparing the password the user entered with the password stored in the db if (doMatch) { console.log('passwords match'); req.session.isLoggedIn = true; req.session.user = user; req.session.save(); res.status(200).json({ isLoggedIn: true, message: 'Logged in!' }); } else { res .status(401) .json({ isLoggedIn: false, message: 'Invalid credentials. Please try again.' }); } }); }); } catch (err) { res.status(400).json({ error: 'Failed to login.' }); } };
控制台输出
password, user.password 123456 $2a$12$9YqCOmycoiLJ8uf7HfL4HebR2hHrJ7b2e6YspG7cP8.6UeAbapHEm
注册控制器代码
const postSignupController = async (req, res) => { const email = req.body.email; const password = req.body.password; try { User.findOne({ email: email }).then((userDoc) => { if (userDoc) { res.status(409).json({ isLoggedIn: false, message: 'The user already exists.' }); return; } else { res.status(200).json({ isLoggedIn: true, message: 'Signed up' }); return bcrypt.hash(password, 12).then((hashedPassword) => { const user = new User({ email: email, password: hashedPassword, cart: { items: [] }, }); return user.save(); }); } }); } catch (err) { res.status(400).json({ error: 'Failed to sign up.' }); } };
问题原因
核心问题出在注册控制器的响应时机错误:你在生成哈希密码并保存用户之前,就提前返回了res.status(200).json(...)。Node.js中响应发送后,后续的异步操作(哈希生成、用户保存)可能会被中断或执行异常,导致数据库中存储的密码并非正确的哈希值(甚至可能是空值或不完整内容),最终登录时bcrypt对比自然失败。
解决方案
1. 调整注册控制器的响应时机
将成功响应移到用户保存完成之后,确保哈希密码正确写入数据库:
const postSignupController = async (req, res) => { const email = req.body.email; const password = req.body.password; try { User.findOne({ email: email }).then((userDoc) => { if (userDoc) { return res.status(409).json({ isLoggedIn: false, message: '该用户已存在。' }); } // 先处理哈希和用户保存,再返回响应 bcrypt.hash(password, 12).then((hashedPassword) => { const user = new User({ email: email, password: hashedPassword, cart: { items: [] }, }); return user.save().then(() => { res.status(200).json({ isLoggedIn: true, message: '注册成功' }); }); }).catch(err => { res.status(400).json({ error: '注册失败。' }); }); }).catch(err => { res.status(400).json({ error: '注册失败。' }); }); } catch (err) { res.status(400).json({ error: '注册失败。' }); } };
2. 优化代码:使用async/await替代then链
统一用async/await能让代码更清晰,也更易处理错误:
// 优化后的注册控制器 const postSignupController = async (req, res) => { try { const { email, password } = req.body; const existingUser = await User.findOne({ email }); if (existingUser) { return res.status(409).json({ isLoggedIn: false, message: '该用户已存在。' }); } const hashedPassword = await bcrypt.hash(password, 12); const user = new User({ email, password: hashedPassword, cart: { items: [] }, }); await user.save(); res.status(200).json({ isLoggedIn: true, message: '注册成功' }); } catch (err) { res.status(400).json({ error: '注册失败。' }); } }; // 优化后的登录控制器 const postLoginController = async (req, res) => { try { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user) { return res.status(401).json({ isLoggedIn: false, message: '无效的登录凭证,请重试。' }); } const doMatch = await bcrypt.compare(password, user.password); if (doMatch) { req.session.isLoggedIn = true; req.session.user = user; await req.session.save(); return res.status(200).json({ isLoggedIn: true, message: '登录成功!' }); } res.status(401).json({ isLoggedIn: false, message: '无效的登录凭证,请重试。' }); } catch (err) { res.status(400).json({ error: '登录失败。' }); } };
3. 额外排查点
- 检查数据库
password字段类型:确保是String类型,且长度至少设置为60(bcrypt哈希固定为60字符),避免字段长度不足导致哈希被截断。 - 处理密码空格:在登录和注册时对密码做
trim()处理,比如const password = req.body.password.trim();,防止用户输入时不小心带入空格引发验证失败。
内容的提问来源于stack exchange,提问作者Yavor
相关产品推荐
相关产品推荐

