我已经解决了这个问题。我找到Express.js role-based permissions middleware
并使用它。太好了,我将重新编写代码!
我想检查多重渗透,但这对我不起作用。
我创建了3个中间件来检查权限:requiredAuth
,checkAdmin
和checkCompanyManager
。
型号: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
)
但这不好玩!我只想使用中间件进行检查,并且不想再次编写代码。我怎样才能做到这一点?我想要你的主意!
答案 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
后,您将无法再发送其他回复。因此,如果checkAdmin
或checkCompanyManager
均失败,则需要阻止它们发送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);
}
}
这应该有效。
恕我直言,上述解决方案感觉过度设计。我个人将使checkAdmin
和checkCompanyManager
的常规函数返回布尔值,然后将它们包装在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
)