Node.js+PostgreSQL登录接口查询返回空rows问题排查
用户登录接口查询返回空数组问题
问题表现
- 服务启动无报错,日志如下:
[nodemon] restarting due to changes... [nodemon] starting `node server.js` Example app listening on port 8080 Connected to database
- 接口能正常接收到请求体
{ username: 'torontofam', password: 'password' },但数据库查询返回rowCount: 0、rows: [],仅返回表字段元信息:
result{ command: 'SELECT', rowCount: 0, oid: null, rows: [], fields: [ Field { name: 'id', tableID: 19247, columnID: 1, dataTypeID: 23, dataTypeSize: 4, dataTypeModifier: -1, format: 'text' }, Field { name: 'username', tableID: 19247, columnID: 2, dataTypeID: 1043, dataTypeSize: -1, dataTypeModifier: 259, format: 'text' }, Field { name: 'password', tableID: 19247, columnID: 3, dataTypeID: 1043, dataTypeSize: -1, dataTypeModifier: 259, format: 'text' }, ] }
- 手动在psql客户端执行带bcrypt哈希密码的查询语句,能正常查到1条匹配记录:
midterm=# SELECT * FROM users WHERE username = 'torontofam' AND password = '$2a$10$FB/BOAVhpuLvpOREQVmvmezD4ED/.JBIDRh70tGevYzYzQgFId2u.';; id | username | password | latitude | longitude ----+------------+--------------------------------------------------------------+------------+------------- 1 | torontofam | $2a$10$FB/BOAVhpuLvpOREQVmvmezD4ED/.JBIDRh70tGevYzYzQgFId2u. | 43.6523736 | -79.3857858 (1 row)
- 发起登录请求后接口返回400,响应信息为
POST /api/users/login 400 26.831 ms - 44 {"message":"Username not found in database"}
原有问题代码
router.post("/login", (req, res) => { const { username, password } = req.body; console.log(req.body); const queryString = `SELECT * FROM users WHERE username = $1 AND password = $2;`; db.query(queryString, [username, password]) .then((data) => { console.log(data); const user = data.rows[0]; if (!user) { return res .status(400) .send({ message: "Username not found in database" }); } const validPassword = bcrypt.compareSync(password, user.password); if (!validPassword) { return res .status(400) .send({ message: "Password does not match username" }); } req.session.user_id = user.id; res.redirect("/"); }) .catch((err) => { res.status(500).json({ error: err.message }); }); });
问题原因
你完全用错了bcrypt的校验流程:
- 数据库里存的password字段是bcrypt生成的哈希串,不是明文
- 你当前的SQL逻辑是拿前端传的明文密码
password,直接和库里存的60位哈希串做等值匹配,两个值根本不相等,自然查不到任何记录 - 你在psql里能查到结果,是因为手动把哈希值写进了查询条件,和接口传明文参数的逻辑完全不一样
- 补充:bcrypt加密时自带随机盐,同一个明文每次生成的哈希值都不同,本身就不支持在SQL层做密码等值匹配。
修复方案
查询阶段只通过用户名匹配用户记录,拿到用户存储的哈希密码后,再用bcrypt的compare方法做密码校验,修正后代码如下:
router.post("/login", (req, res) => { const { username, password } = req.body; console.log(req.body); // 去掉SQL里的密码等值判断,仅按用户名查询 const queryString = `SELECT * FROM users WHERE username = $1;`; db.query(queryString, [username]) .then((data) => { console.log(data); const user = data.rows[0]; if (!user) { return res .status(400) .send({ message: "Username not found in database" }); } // 对比明文密码和数据库存储的哈希密码 const validPassword = bcrypt.compareSync(password, user.password); if (!validPassword) { return res .status(400) .send({ message: "Password does not match username" }); } req.session.user_id = user.id; res.redirect("/"); }) .catch((err) => { res.status(500).json({ error: err.message }); }); });
内容的提问来源于stack exchange,提问作者jhoangqm
相关产品推荐
相关产品推荐

