node实现管理后台权限控制

584 阅读2分钟

前言

管理后台权限控制是使用了RBAC模型,前端是vue,后端是eggjs。

RBAC(Role-Based Access Control)模型是一种用于访问控制的权限管理模型。
在 RBAC 模型中,权限的分配和管理是基于角色进行的。
RBAC 模型包含用户(User)角色(Role)权限(Permission)

表设计

权限表permissions image.png

角色表roles及其用户-角色中间表,角色-权限中间表

image.png

管理后台配置权限页面效果

权限管理列表:
这里需要手动把所有路由和权限 按层级录入,这里后端需要提供一个查询组装好的树结构的接口 image.png

角色管理列表:
这里可以创建角色相关信息,然后给角色 权限配置和成员管理

image.png 权限配置,给该角色勾选权限码提交 image.png 成员管理,给该角色勾选用户提交 image.png

后端接口实现

这里使用eggjs框架,用egg-sequelize作为ORM库。相关model表关联实现,角色表roles查询 角色的权限配置和获取用户权限getUserInfo方法 看我之前的文章: sequelize多对多关联实现
这里补充一下其他相关

// 角色表roles删除方法,需要把中间表关联数据删除
async del() {
    const { ctx, app } = this;
    const reqBody = ctx.request.body;
    const model = ctx.model.Roles;
    const { id } = reqBody;
    try {
      // 查找该元数据
      const exitItem = await model.findOne({
        where: { id }
      });
      if (!exitItem) {
        return ctx.error(4002, '当前数据不存在');
      }
      // 记录删除人操作信息
      await exitItem.update({
        updateUser: ctx.userInfo.id,
        updateUserName: ctx.userInfo.username,
      });
      // 删除所有关联数据
      exitItem.setPermissions([])
      // 删除
      await exitItem.destroy();
      ctx.success({ id });
    } catch (error) {
      ctx.error(4002, error);
    }
  }

权限表permissions没复杂接口,都是常规的增删改查

// 权限表permissions 数据的组装成 树结构
async getTreelist() {
    const { ctx, app } = this;
    const model = ctx.model.Permissions;
    const { type } = query;
    const tableQuery = {
      raw: true,
      where: {
        type: type || 'admin'
      },
      attributes: {
        exclude: ['deleted_at']
      }
    };
    try {
      const data = await model.findAll(tableQuery);
      const convertToTree = (arr, parentId = null) => {
        const result = [];
      
        for (const item of arr) {
          if (item.parentId === parentId) {
            const children = convertToTree(arr, item.id);
            result.push({
              ...item,
              children
            });
          }
        }
      
        return result;
      };
      const tree = convertToTree(data);
      ctx.success(tree);
    } catch (error) {
      ctx.error(4002, error);
    }
  }

登录通过getUserInfo方法返回了所有权限码,前端vue项目在路由拦截器上判断,第一次初始化时获取用户信息接口后,根据router.addRoutes方法把成功匹配的路由动态增加。按钮级别的权限码在页面上用v-permission自定义指令校验,下面是vue的自定义指令实现

// 路径 src/directive/permission.js
import { hasPermission } from '@/utils/auth'
// 权限控制指令
async function checkPermission(el, binding) {
  const { value } = binding
  const isOk = await hasPermission(value) // 这里会把权限code和所有权限码进行匹配,如果包含则返回true
  if (!isOk) {
    el.parentNode && el.parentNode.removeChild(el)
  }
}

export default {
  inserted(el, binding) {
    checkPermission(el, binding)
  },
  update(el, binding) {
    checkPermission(el, binding)
  }
}

---------------------------------------------------------------------
// 路径 src/directive/index.js
import Vue from 'vue';
import permission from './permission';
// 权限控制
Vue.directive('permission', permission);