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) => { /* 查询自己的订单逻辑 */ })
必须补全的越权防护逻辑
光改中间件还不够,业务层必须加防越权处理,不然会出现普通用户查看/修改他人订单的问题:
- 普通用户查询订单时,必须强制过滤当前用户ID,不能返回全量订单:
router.get("/booking/my", auth, async (req, res) => { // 只查当前登录用户自己的订单 const myBookings = await Booking.find({ userId: req.user._id }) res.send(myBookings) })
- 用户提交订票请求时,强制把订单关联的用户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
相关产品推荐
相关产品推荐

