acl文档node
Q: 这个工具用来作什么的呢git
A: 用户有不一样的权限,好比管理员,vip,普通用户,每一个用户对应访问api,页面都不同github
nodejs有两个比较有名的权限管理模块 一个是acl 一个是rbac 综合对比了一下最终在作项目的时候选择了acl数据库
const Acl = require('acl');
const aclConfig = require('../conf/acl_conf');
module.exports = function (app, express) {
const acl = new Acl(new Acl.memoryBackend()); // eslint-disable-line
acl.allow(aclConfig);
return acl;
};
// acl_conf
module.exports = [
{
roles: 'normal', // 通常用户
allows: [
{ resources: ['/admin/reserve'], permissions: ['get'] },
]
},
{
roles: 'member', // 会员
allows: [
{ resources: ['/admin/reserve', '/admin/sign'], permissions: ['get'] },
{ resources: ['/admin/reserve/add-visitor', '/admin/reserve/add-visitor-excel', '/admin/reserve/audit', '/admin/sign/ban'], permissions: ['post'] },
]
},
{
roles: 'admin', // 管理
allows: [
{ resources: ['/admin/reserve', '/admin/sign', '/admin/set'], permissions: ['get'] },
{ resources: ['/admin/set/add-user', '/admin/set/modify-user'], permissions: ['post'] },
]
},
{
roles: 'root', // 最高权限
allows: [
{ resources: ['/admin/reserve', '/admin/sign', '/admin/set'], permissions: ['get'] },
]
}
];
复制代码
这里是结合express作校检...结果发现acl本身提供的中间件太鸡肋了,这里就重写了一个。express
function auth() {
return async function (req, res, next) {
let resource = req.baseUrl;
if (req.route) { // 正常在control中使用有route属性 可是使用app.use则不会有
resource = resource + req.route.path;
}
console.log('resource', resource);
// 容错 若是访问的是 /admin/sign/ 后面为 /符号认定也为过
if (resource[resource.length - 1] === '/') {
resource = resource.slice(0, -1);
}
let role = await acl.hasRole(req.session.userName, 'root');
if (role) {
return next();
}
let result = await acl.isAllowed(req.session.userName, resource, req.method.toLowerCase());
// if (!result) {
// let err = {
// errorCode: 401,
// message: '用户未受权访问',
// };
// return res.status(401).send(err.message);
// }
next();
};
}
复制代码
有点要说明的是express.Router支持导出一个Router模块 再在app.use使用,可是若是你这样使用app.use('/admin/user',auth(), userRoute);
那么是在auth这个函数是获取不到req.route
这个属性的。 由于acl对访问权限作的是强匹配,因此须要有必定的容错api
result为数据库查询出来的用户信息,或者后台api返给的用户信息,这里的switch可使用配置文件的形式,由于我这边本次项目只有三个权限,因此就在这里简单写了一下。数组
let roleName = 'normal';
switch (result.result.privilege) {
case 0:
roleName = 'admin';
break;
case 1:
roleName = 'normal';
break;
case 2:
roleName = 'member';
break;
}
if (result.result.name === 'Nathan') {
roleName = 'root';
}
req.session['role'] = roleName;
// req.session['role'] = 'root'; // test
acl.addUserRoles(result.result.name, roleName);
// acl.addUserRoles(result.result.name, 'root'); // test
复制代码
在 express+pug中app.locals.auth= async function(){}
这个写法在pug渲染的时候是不会得出最终结果的,由于pug是同步的,那么我如何控制当前页面或者说当前页面的按钮用户是否有权限展现出来, 这里通用的作法有bash
我这里采用的是结局方案2.由于比较方便, 可是问题来了 express+pug是不支持异步的写法,而acl提供给咱们的全是异步的, 由于时间缘由,我没有去深究里面的判断,而是采用了一种耦合性比较高可是比较方便的判断方法.session
app.locals.hasRole = function (userRole, path, method = 'get') {
if (userRole === 'root') {
return true;
}
const current = aclConf.find((n) => {
return n['roles'] === userRole;
});
let isFind = false;
for (let i of current.allows) {
const currentPath = i.resources; // 目前数组第一个为单纯的get路由
isFind = currentPath.includes(path);
if (isFind) {
// 若是找到包含该路径 而且method也对应得上 那么则经过
if (i.permissions.includes(method)) {
break;
}
// 若是找到该路径 可是method对应不上 则继续找.
continue;
}
}
return isFind;
};
复制代码
上述代码页比较简单, 去遍历acl_conf,查找用户是否有当前页面的或者按钮的权限 由于acl_conf在加载的时候就已经被写入内存了,因此性能消耗不会特别大。好比下面的例子。app
if hasRole(user.role, '/admin/reserve/audit', 'post')
.col.l3.right-align
a.waves-effect.waves-light.btn.margin-right.blue.font12.js-reviewe-ok 赞成
a.waves-effect.waves-light.btn.pink.accent-3.font12.js-reviewe-no 拒绝
复制代码
依靠acl这个组件能够快速打造一个用户的权限管理模块。 可是还有个问题 也急速那个app.locals.hasRole函数, 若是你使用removeAllow动态改变了用户的权限表,那么hasRole函数就很麻烦了。 因此在这种状况下 有如下几个解决方案
const hasBtn1Role = hasRole(user.role, '/xxx','get');
res.render('a.pug',{hasBtn1Role})
复制代码