【问题标题】:Check multi permission by middleware通过中间件检查多权限
【发布时间】:2019-07-31 08:38:20
【问题描述】:

我已经解决了这个问题。我找到Express.js role-based permissions middleware 并使用它。太好了,我会重写我的代码!


我想检查多重权限,但它不适合我。

我创建了 3 个中间件来检查权限:requiredAuthcheckAdmincheckCompanyManager

型号:User:{name,permistion,isBlocked,公司:{id,userPermistion}}

requiredAuth函数会检查并找到signedUser,并将其设置为res.locals.user

const checkAdmin = (req, res, next) => {
    let user = res.locals.user
    if (user.permission === 2) next()
    else res.json({errors: "Only admin can do this action"})
}

const checkCompanyManager = (req, res, next) => {
    let user = res.locals.user
    let companyId = req.body.companyId ? req.body.companyId : req.query.companyId
    if (user.company.id && user.company.id.equals(companyId)
        && user.company.userPermistion === 1) next()
    else res.json({errors: "Only company member can do this action"})
}

最后,我使用路由器中的 all 来检查操作阻止用户(只有管理员或公司经理可以阻止用户)

router.post('/admin/block-by-ids',
    requiredAuth,
    checkAdmin || checkCompanyManager,
    userController.blockByIds
)

但是不行,因为如果checkAdmin错了,就是break返回json,不运行checkCompanyManager我可以这样解决这个问题:

router.post('/admin/block-by-ids',
    requiredAuth,
    (req, res, next) => {
        let user = res.locals.user
        let companyId = req.body.companyId
        if ((user.permission === 2) ||
            (user.company.id && user.company.id.equals(companyId) &&
                user.company.userPermistion === 1)) {
            next()
        } else next("Only admin or company manager can do this action")
    },
    userController.blockByIds
)

但这并不好玩!我只想用中间件检查,不想再写代码。我怎样才能做到这一点?我想要你的想法!

【问题讨论】:

  • 您意识到|| 运算符被定义为返回第一个真值吗?由于checkAdmincheckCompanyManager 都是函数,它们都是真的,因此|| 运算符返回checkAdmin

标签: javascript node.js express mongoose


【解决方案1】:

|| 运算符并没有按照您的想法执行。它返回第一个真值:

var a = 1 || 2; // a is 1

您需要的是一个 OR 中间件。比如:

function or (middleware1, middleware2) {
    return function (req, res, next) {
        var alreadyCalledNext = false;
        function resolve () {
            if (!alreadyCalledNext) {
                alreadyCalledNext = true;
                next();
            }
        }
        middleware1(req,res,resolve);
        middleware2(req,res,resolve);
    }        
}

router.post('/admin/block-by-ids',
    requiredAuth,
    or(checkAdmin, checkCompanyManager),
    userController.blockByIds
)

但是上面的实现遇到了另一个问题。发送 res.json 后,您将无法再发送回复。因此,如果checkAdmincheckCompanyManager 失败,您需要阻止它们发送res.json,除非两者都失败。所以你需要存根res并传递一个假的res(就像我们上面对next所做的那样):

function or (middleware1, middleware2) {
    return function (req, res, next) {
        var alreadyCalledNext = false;
        function resolve () {
            if (!alreadyCalledNext) {
                alreadyCalledNext = true;
                next();
            }
        }
        var jsonCount = 0;
        var fakeRes = {
            locals: res.locals,
            json: function (data) {
                jsonCount ++;
                if (jsonCount >= 2) { // both must fail for OR to fail
                    res.json(data);
                }
            }
        }

        middleware1(req,fakeRes,resolve);
        middleware2(req,fakeRes,resolve);
    }        
}

这应该可行。


恕我直言,上面的解决方案感觉设计过度。我个人会让checkAdmincheckCompanyManager 常规函数返回布尔值,然后将它们包装在checkPermissions 中间件中:

const isAdmin = (req,res) => {
    let user = res.locals.user
    return user.permission === 2
}

const isCompanyManager = (req,res) => {
    let user = res.locals.user
    let companyId = req.body.companyId ? req.body.companyId : req.query.companyId
    return user.company.id && user.company.id.equals(companyId) && user.company.userPermistion === 1
}

const checkPermissions = function (checks) {
    return (req, res, next) => {
        // Call next if any check passes:
        for (let i=0; i<checks.length; i++) {
            if (checks[i](req,res)) return next();
        }

        res.json({errors: "You don't have authorization to do this action"})
    }
}

router.post('/admin/block-by-ids',
    requiredAuth,
    checkPermissions([isAdmin, isCompanyManager]),
    userController.blockByIds
)

【讨论】:

  • 请注意,根据 OR 逻辑的性质,您无法区分公司经理或管理员的错误消息,因为两者都必须失败才能发生错误(除非您添加一个复选框,询问用户是管理员还是经理或以某种方式开发读心软件)
  • 非常感谢!我会再做一次!你真好!
  • @NguyenVanTuan - 我已经修改了代码以使其更易于重用 - 这样您就可以拥有只能由管理员访问或只能由公司经理访问的端点
  • 很漂亮。您的代码很容易重用。谢谢!
猜你喜欢
  • 2012-09-06
  • 2021-09-05
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2020-07-01
  • 2010-09-06
  • 1970-01-01
相关资源
最近更新 更多