Node.js如何实现仅管理员可提交数据写入数据库的权限控制
问题根因
- 接口内的用户查询逻辑完全错误:
await User.find({ admin: true })是查询全表所有管理员账号,只要系统中存在任意一个管理员账号,该查询就会返回结果,和当前发起请求的登录用户没有任何关系,等于完全没做权限判断。 - 布尔判断逻辑错误:
User.find()返回值永远是数组,哪怕查询无匹配结果也会返回空数组[],而JS中空数组的布尔值为true,因此if (user)判断永远成立,所有请求都会被放行。 - 身份认证中间件存在隐性bug:代码中
throw new error()的构造函数首字母未大写,JS内置错误构造函数为Error,小写写法会触发引用错误。
正确实现方案
你的身份认证中间件已经将当前登录用户挂载到了req.user属性上,不需要在业务接口中重复查询用户,直接基于该对象做权限判断即可,推荐将管理员权限校验抽为独立可复用的中间件,避免重复代码。
1. 新增管理员权限校验中间件
const requireAdmin = (req, res, next) => { // 直接取auth中间件挂载的当前登录用户判断权限 if (!req.user?.admin) { // 无操作权限返回403状态码,符合HTTP语义:401代表未认证,403代表已认证但无权限 return res.status(403).send("You are not allowed to perform this action"); } next(); };
2. 修正身份认证中间件的bug
const authentication = async (req, res, next) => { try { const token = req.header("Authorization").replace("Bearer ", ""); const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY); const user = await User.findOne({ _id: decoded._id, "tokens.token": token }); if (!user) { throw new Error(); // 修正首字母大写问题 } req.token = token req.user = user next(); } catch (e) { res.status(401).send("Authentication failed"); } };
3. 修正业务接口逻辑
// 中间件按顺序执行:先校验登录身份,再校验管理员权限 router.post("/bus/add", auth, requireAdmin, async (req, res) => { try { const bus = new Bus(req.body); await bus.save(); res.status(201).send(bus); } catch (e) { res.status(500).json({ message: "Please enter the valid data", }); } });
注意事项
- 所有权限校验必须绑定当前登录上下文的用户身份,禁止使用无用户维度筛选的全局查询做权限判断,这类写法等于完全不设防。
- 查询单个用户时优先使用
findOne(),避免find()返回数组带来的空值判断坑点。 - 权限校验逻辑尽量抽为公共中间件,避免每个业务接口重复编写,减少漏加校验、逻辑写错的概率。
内容的提问来源于stack exchange,提问作者user19038939
相关产品推荐
相关产品推荐

