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

Owner认证权限异常:餐厅Dashboard无法正常访问

权限认证问题修复方案

问题场景

使用mysql2开发时,Owner已关联对应餐厅,但无法访问该餐厅的Dashboard。日志显示req.owner.id与restaurant.owner_id数值均为7,但权限判断if (req.owner && req.owner.id === restaurant.owner_id)不通过。

数据库表关联规则:

  • owner表以user_id为外键关联user表主键
  • restaurant表以owner_id为外键关联owner表主键,支持一个Owner管理多家餐厅

核心原因分析

从中间件代码和日志可以确定:req.owner.id实际为undefined,导致与restaurant.owner_id(数字7)不匹配。
在isLoggedInAdmin中间件中,db.promise().query返回格式是[结果行数组, 字段信息],你通过const [owner] = await db.promise().query(...)将结果行数组赋值给了owner变量(日志显示Owner found: [ { id: 7, user_id: 42 } ],owner是数组),但后续直接取owner.id——数组没有id属性,因此req.owner.id为undefined,自然无法匹配数字7。

修复步骤

1. 修正中间件的Owner数据获取逻辑

修改isLoggedInAdmin中间件中Owner查询和赋值的代码:

exports.isLoggedInAdmin = async (req, res, next) => {
    console.log('isLoggedInAdmin middleware called');
  
    if (req.cookies.one) {
      try {
        const decode = await jwt.verify(req.cookies.one, process.env.JWT_SECRET);
  
        // 获取用户信息,解构后取数组第一个元素
        const [userRows] = await db.promise().query("SELECT * FROM user WHERE id = ?", [decode.id]);
        const user = userRows[0];
        if (!user) {
          return res.status(401).json({ message: "Unauthorized" });
        }
  
        // 获取Owner信息,同样取数组第一个元素
        const [ownerRows] = await db.promise().query("SELECT * FROM owner WHERE user_id = ?", [decode.id]);
        const owner = ownerRows[0];
        if (!owner) {
          return res.status(401).json({ message: "Unauthorized" });
        }
  
        if (user.id !== owner.user_id) {
          return res.status(401).json({ message: "Unauthorized" });
        }
  
        console.log('Owner found:', owner);
        req.owner = {
          id: owner.id,
          user_id: owner.user_id,
        };
        next();
      } catch (error) {
        console.error(error);
        return res.status(401).json({ message: "Unauthorized" });
      }
    } else {
      console.log('No owner found');
      next();
    }
};

2. 可选:优化路由查询性能

原路由中两次查询数据库可合并为一次关联查询,减少数据库请求次数:

router.get('/restaurants/:name/:location/dashboard', userContoller.isLoggedInAdmin, async (req, res) => {
  const restaurantName = req.params.name;
  const restaurantLocation = req.params.location;

  console.log(`restaurantName: ${restaurantName}`);
  console.log(`restaurantLocation: ${restaurantLocation}`);

  try {
    // 一次查询获取餐厅及对应Owner信息
    const [rows] = await db.promise().query(
      `SELECT r.*, o.* 
       FROM restaurant r 
       JOIN owner o ON r.owner_id = o.id 
       WHERE r.name = ? AND r.location = ?`,
      [restaurantName, restaurantLocation]
    );

    if (!rows.length) {
      return res.render('errors/no permission');
    }

    const restaurant = rows[0];
    console.log(`restaurant: ${JSON.stringify(restaurant)}`);

    // 权限判断
    if (req.owner && req.owner.id === restaurant.owner_id) {
      res.render('role/admin/index', { 
        owner: { id: restaurant.id, user_id: restaurant.user_id }, 
        restaurant 
      });
    } else {
      console.log('Owner not granted access to dashboard');
      res.render('errors/no permission');
    }
  } catch (error) {
    console.error(error);
    res.sendStatus(500);
  }
});

额外优化建议

  • 登录逻辑中redirect("/restaurants/:name/:location/dashboard")是无效的,:name和:location是路由参数占位符,需要替换为用户实际管理的餐厅名称和地址,否则会跳转到字面量路径
  • 权限判断前先校验restaurant是否存在,避免因餐厅不存在导致restaurant.owner_id报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 06:15:46