You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS中如何为非管理员注册用户开放指定数据的POST/GET接口权限

问题根源

你当前的认证中间件把「身份合法性校验」和「管理员权限校验」耦合到一起了,查询用户时硬编码了admin: true的筛选条件,所有token合法但非管理员的用户都会直接被拦在接口外,自然访问不了booking的GET、POST接口。

实现方案

拆分两层中间件,第一层只校验登录身份是否合法,第二层单独做管理员权限判断,不同接口按需挂载对应中间件即可。

1. 重写基础身份认证中间件

这层只做JWT合法性校验、查询对应用户信息,不做权限判断,只要是登录状态有效的用户(不管是不是管理员)都能放行:

// 基础认证中间件:所有需要登录的接口都挂这个
const authentication = async (req, res, next) => {
  try {
    const token = req.header("Authorization").replace("Bearer ", "");
    const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY);
    // 去掉admin: true的筛选条件,只要token匹配对应用户就通过
    const user = await User.findOne({ _id: decoded._id, "tokens.token": token });

    if (!user) {
      throw new Error(); // 注意Error首字母大写,原写法会触发引用错误
    }

    req.token = token
    req.user = user
    next();
  } catch (e) {
    res.status(401).send({ error: "请先登录" });
  }
};

2. 新增管理员权限校验中间件

这层专门判断当前登录用户是不是管理员,挂在需要管理员权限的接口上:

// 管理员权限中间件:仅管理员可访问的接口在基础auth之后挂这个
const requireAdmin = async (req, res, next) => {
  if (!req.user.admin) {
    return res.status(403).send({ error: "无管理员权限" });
  }
  next();
};

3. 按权限规则配置路由

不同接口按需挂载中间件即可,完全匹配需求:

  • bus、trip、location类的所有接口:挂载authentication + requireAdmin,仅管理员可访问
  • booking表的PATCH、DELETE接口:挂载authentication + requireAdmin,仅管理员可修改/删除订单
  • booking表的GET、POST接口:只挂载authentication,所有登录用户(管理员+普通注册用户)都可访问

路由配置示例:

// 管理员专属接口
router.get("/bus", auth, requireAdmin, async (req, res) => { /* 公交列表查询逻辑 */ })
router.patch("/booking/:id", auth, requireAdmin, async (req, res) => { /* 修改订单逻辑 */ })
router.delete("/booking/:id", auth, requireAdmin, async (req, res) => { /* 删除订单逻辑 */ })

// 所有登录用户可访问的booking接口
router.post("/bus/book", auth, async (req, res) => { /* 订票逻辑 */ })
router.get("/booking/my", auth, async (req, res) => { /* 查询自己的订单逻辑 */ })
必须补全的越权防护逻辑

光改中间件还不够,业务层必须加防越权处理,不然会出现普通用户查看/修改他人订单的问题:

  1. 普通用户查询订单时,必须强制过滤当前用户ID,不能返回全量订单:
router.get("/booking/my", auth, async (req, res) => {
  // 只查当前登录用户自己的订单
  const myBookings = await Booking.find({ userId: req.user._id })
  res.send(myBookings)
})
  1. 用户提交订票请求时,强制把订单关联的用户ID设为当前登录用户ID,不要信任前端传的userId字段:
router.post("/bus/book", auth, async (req, res) => {
  // 强制绑定当前登录用户ID,防止前端伪造他人订单
  const bookBus = new Booking({
    ...req.body,
    userId: req.user._id
  });
  // 修正原座位重复判断逻辑:查数据库是否存在同班次同座位的有效订单
  const seatIsBooked = await Booking.findOne({ 
    tripId: req.body.tripId, 
    seatNumbers: req.body.seatNumbers 
  })
  if(seatIsBooked){
    return res.status(400).send('该座位已被预订')
  }
  try {
    // 注意是调用实例的save方法,不是模型本身的save
    const book = await bookBus.save();
    res.status(201).send(book);
  } catch (e) {
    res.status(500).send(e);
  }
});

内容的提问来源于stack exchange,提问作者user19038939

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 21:24:30