通过中间件检查多重权限

时间:2019-07-31 08:38:20

标签: javascript node.js express mongoose

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


我想检查多重渗透,但这对我不起作用。

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

型号:User:{名称,权限,被阻止,公司:{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"})
}

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

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

但是它不起作用,因为如果checkAdmin错误,它将中断并返回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
)

但这不好玩!我只想使用中间件进行检查,并且不想再次编写代码。我怎样才能做到这一点?我想要你的主意!

1 个答案:

答案 0 :(得分:2)

||运算符没有执行您认为的操作。它返回第一个真实值:

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
)