Express中bcrypt.compareSync参数undefined错误及登录验证求助
从你给出的错误信息 Error: Illegal arguments: undefined, undefined at Object.bcrypt.compareSync 来看,问题出在调用bcrypt.compareSync时,传入的password或auser.passHash其中一个(或两个)是undefined。下面我分步骤帮你排查和解决:
第一步:检查前端表单的输入框命名
首先要确认你的Nunjucks模板里,用户名和密码输入框的name属性是否准确设置为username和password。Express的urlencoded解析器是根据表单元素的name属性来填充req.body的,如果命名不匹配,req.body.username和req.body.password就会是undefined。
正确的表单示例应该是这样:
<form method="POST" action="/user"> <input type="text" name="username" required placeholder="请输入用户名"> <input type="password" name="password" required placeholder="请输入密码"> <button type="submit">登录</button> </form>
你可以先查看console.log(req.body)的输出,确认是否能拿到正确的用户名和密码值,如果输出是空对象或者缺少这两个字段,那就是表单命名的问题。
第二步:修复NeDB查询的异步问题
你使用的DataStore应该是NeDB的实例,它的find()方法默认是异步的——如果不带回调函数,它返回的是一个查询游标,不是直接的用户数据,这就导致auser是undefined,自然auser.passHash也不存在。
针对这个问题,有两种修复方案:
方案1:改用同步查询(NeDB v2+支持)
如果你的NeDB版本支持同步方法,可以直接用findOneSync()替代find()(登录场景只需要匹配一个用户,用findOneSync更合适):
let auser = usersdb.findOneSync({ username: username });
方案2:改用异步查询(更符合Node.js最佳实践)
Node.js生态更推荐异步风格,用findOne()配合回调来处理:
app.post('/user', express.urlencoded({extended:true}), function (req, res) { console.log(req.body); let username = req.body.username; let password = req.body.password; // 先做参数校验,避免undefined传入后续逻辑 if (!username || !password) { return res.render("loginError.njk"); } // 异步查询用户 usersdb.findOne({ username: username }, function (err, auser) { // 处理数据库查询错误 if (err) { console.error('数据库查询错误:', err); return res.render("loginError.njk"); } // 用户不存在 if (!auser) { return res.render("loginError.njk"); } try { // 验证密码 let verified = bcrypt.compareSync(password, auser.passHash); if (verified) { let oldInfo = req.session.user; req.session.regenerate(function (err) { if (err) { console.log('Session生成错误:', err); return res.render("loginError.njk"); } req.session.user = Object.assign(oldInfo, auser, { loggedin: true }); res.render("welcome.njk", {user: auser}); }); } else { // 密码不匹配 res.render("loginError.njk"); } } catch (bcryptErr) { console.error('密码验证错误:', bcryptErr); res.render("loginError.njk"); } }); });
第三步:额外的边界处理建议
- 添加参数校验:在查询数据库前先检查
username和password是否存在,避免无效请求进入数据库查询环节; - 捕获bcrypt的异常:虽然
compareSync是同步方法,但如果传入的哈希值格式不正确,也会抛出错误,用try-catch包裹可以避免程序崩溃; - 使用
findOne()替代find():登录场景只需要匹配一个用户,findOne()更高效,也不会返回数组导致后续处理出错。
按照上面的步骤排查和修复,应该就能解决你遇到的登录验证问题了。
内容的提问来源于stack exchange,提问作者Bruthon1

