nodejs的用户权限管理——acl.md

acl文档node

说明

Q: 这个工具用来作什么的呢git

A: 用户有不一样的权限,好比管理员,vip,普通用户,每一个用户对应访问api,页面都不同github

nodejs有两个比较有名的权限管理模块 一个是acl 一个是rbac 综合对比了一下最终在作项目的时候选择了acl数据库

使用方法

  1. 创建起配置文件
  2. 用户登陆后分配相应的权限
  3. 须要控制的地方使用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
复制代码

pug页面中的渲染逻辑控制

在 express+pug中app.locals.auth= async function(){}这个写法在pug渲染的时候是不会得出最终结果的,由于pug是同步的,那么我如何控制当前页面或者说当前页面的按钮用户是否有权限展现出来, 这里通用的作法有bash

  1. 用户在登陆的时候有一个路由表和组件表 而后在渲染的时候 根据这个表去渲染
  2. 在须要权限控制的地方,使用函数来判断用户是否有权限访问

我这里采用的是结局方案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函数就很麻烦了。 因此在这种状况下 有如下几个解决方案

  1. 从acl源码入手
  2. 每次渲染的时候就把数据准备好
const hasBtn1Role = hasRole(user.role, '/xxx','get');
res.render('a.pug',{hasBtn1Role})
复制代码
相关文章
相关标签/搜索