资讯专栏INFORMATION COLUMN

利用 Vue.js 实现前后端分离的RBAC角色权限管理

BingqiChen / 1661人阅读

摘要:项目背景物业管理后台,不同角色拥有不同权限采用技术实现权限管理需要后端接口支持,这里仅提供前端解决方案。前端代码可参考如果有更好的想法和建议,欢迎评论。

项目背景:物业管理后台,不同角色拥有不同权限

采用技术:Vue.js + Vuex + Element UI

实现 RBAC 权限管理需要后端接口支持,这里仅提供前端解决方案。
因代码篇幅较大,对代码进行了删减,文中 “...” 即为省略的一部分代码。
大致思路:

首先登录成功后,从后台拉取用户当前可显示的菜单和可用权限列表,分别将其存入 store 的 nav(菜单导航) 和 auth(用户可用权限) 中,
在用户切换路由时,判断是否存在 auth ,如果不存在,则重新获取,判断当前访问地址 to.meta.alias 是否在
用户可用权限列表中,如果不存在,则提示无权限,否则进入路由。

1. 路由与侧边菜单分离 侧边菜单相关代码 Main.vue




2. 路由切换前进行鉴权

路由定义的部分代码,对每个路由添加了 meta 属性,用于鉴权。
这里 component 采用了异步引入的方式。

定义路由
// ...
// 系统管理
{
path: "/system",
component: Main,
name: "系统管理",
redirect: "/system/organization",
children: [{
  path: "/system/organization",
  component: () => import ("@/views/System/Organization.vue"),
  name: "组织结构",
  // requiresAuth 用于确认此地址是否需要验证
  // alias 用于获取后端返回rbac权限对应的前端路由地址和导航菜单图标
  meta: {requiresAuth: true, alias: "Pmsadmin/Oragnize/list"}
},
  {
    path: "/system/user",
    component: () => import ("@/views/System/User.vue"),
    name: "人员管理",
    redirect: "/system/user/index",
    children: [
    {
      path: "/system/user/index",
      component: () => import ("@/views/System/UserList.vue"),
      name: "职员列表",
      meta: {requiresAuth: true, alias: "Pmsadmin/Admin/list"}
    }
    ]
  },
  {
    path: "/system/auth",
    component: () => import ("@/views/System/Auth.vue"),
    name: "角色管理",
    meta: {requiresAuth: true, alias: "Pmsadmin/Role/list"}
  }
]
}
// ...
路由钩子 beforeEach
router.beforeEach((to, from, next) => {
  document.title = `${configs.title} - ${to.name}`;
  const {hasAuth, auth} = store.state.user;
  // 未拿到权限,则获取
  if (!hasAuth) {
    store.dispatch("getUserAuth");
    console.log("重新获取用户权限");
    // next();
  }
  // 如果未登录,跳转
  if (window.localStorage.getItem("IS_LOGIN") === null && to.path !== "/login") {
    console.log("未登录状态");
    next({
      path: "/login",
      query: {redirect: to.fullPath}
      // 将跳转的路由path作为参数,登录成功后跳转到该路由
    })
  } else {
    // 需要鉴权的路由地址
    console.log(to, auth.indexOf(to.meta.alias), auth);
    if (to.meta.requiresAuth) {
      if (auth.indexOf(to.meta.alias) > -1) {
        console.log("有权限进入");
        next();
      } else {
        if(auth.length > 0) {
          Message.error({
            message: "当前用户权限不足,无法访问",
            showClose: true,
          });
        } else {
          next();
        }
      }
    } else {
      next();
    }
  }
});
在 Vuex 的 state 中,定义好 nav 对象
// 登录用户信息
const user = {
  name: "", // 用户名
  avatar: "", // 用户头像
  auth: [], // 用户权限
  hasAuth: false // 是否已经加载用户权限
};
// 导航菜单
const nav = [];
通过 action 异步获取数据
// 获取用户权限
const getUserAuth = async ({commit}) => {
  const res = await http.post("YOUR_URL", {});
  if (res === null) return;
  console.log("getUserAuth", res.param);
  commit("SET_USER_AUTH", res.param.auth);
  commit("SET_SIDE_NAV", res.param.nav);
};
Vuex 中的 mutation 的相关代码
// 设置用户权限
const SET_USER_AUTH = (state, auth) => {
  state.user.auth = auth.concat("欢迎使用");
  state.user.hasAuth = true;
};
// 设置导航菜单
const SET_SIDE_NAV = (state, nav) => {
  // 导航菜单
  let _nav = [{
    name: "欢迎使用",
    url: "/main",
    iconCls: "fa fa-bookmark"
  }];
  // 权限菜单对应的路由地址
  const route = {
    "系统管理": {iconCls: "fa fa-archive", url: ""},
    "Pmsadmin/Oragnize/list": {iconCls: "", url: "/system/organization"},
    "Pmsadmin/Admin/list": {iconCls: "", url: "/system/user/index"},
    "Pmsadmin/Role/list": {iconCls: "", url: "/system/auth"},
    "Pmsadmin/Log/record": {iconCls: "", url: "/system/logs"},
    "项目管理": {iconCls: "fa fa-unlock-alt", url: ""},
    "Pmsadmin/Project/list": {iconCls: "", url: "/project/list/index"},
    "Pmsadmin/House/list": {iconCls: "", url: "/project/house"},
    "Pmsadmin/Pack/list": {iconCls: "", url: "/project/pack"},
    "广告位": {iconCls: "fa fa-edit", url: ""},
    "Pmsadmin/Place/list": {iconCls: "", url: "/adsplace/list"},
    "投诉建议": {iconCls: "fa fa-tasks", url: ""},
    "Pmsadmin/Scategory/list": {iconCls: "", url: "/complain/type"},
    "Pmsadmin/Complain/list": {iconCls: "", url: "/complain/list"},
    "Pmsadmin/Suggest/list": {iconCls: "", url: "/complain/suggestion"},
    "报事报修": {iconCls: "fa fa-user", url: ""},
    "Pmsadmin/Rcategory/list": {iconCls: "", url: "/rcategory/type"},
    "Pmsadmin/Rcategory/info": {iconCls: "", url: "/rcategory/public"},
    "Pmsadmin/Repair/list": {iconCls: "", url: "/rcategory/personal"},
    "便民服务": {iconCls: "fa fa-external-link", url: ""},
    "Pmsadmin/Bcategory/list": {iconCls: "", url: "/bcategory/type"},
    "Pmsadmin/Service/list": {iconCls: "", url: "/bcategory/list"},
    "首座推荐": {iconCls: "fa fa-file-text", url: ""},
    "Pmsadmin/stcategory/list": {iconCls: "", url: "/stcategory/type"},
    "Pmsadmin/Store/list": {iconCls: "", url: "/stcategory/list"},
    "招商租赁": {iconCls: "fa fa-leaf", url: ""},
    "Pmsadmin/Bussiness/list": {iconCls: "", url: "/bussiness/list"},
    "Pmsadmin/Company/list": {iconCls: "", url: "/bussiness/company"},
    "Pmsadmin/Question/list": {iconCls: "", url: "/bussiness/question"},
    "停车找车": {iconCls: "fa fa-ra", url: ""},
    "Pmsadmin/Cplace/list": {iconCls: "", url: "/cplace/cmanage"},
    "Pmsadmin/Clist/list": {iconCls: "", url: "/cplace/clist"},
    "Pmsadmin/Cquestion/list": {iconCls: "", url: "/cplace/cquestion"},
  };
  for (let key in nav) {
    let item = nav[key];
    let _temp = {};
    let subItems = []; // 二级菜单临时数组
    if (item.children && item.children.length > 0) {
      // 二级菜单
      item.children.forEach(subItem => {
        subItems.push(Object.assign({}, {
          name: subItem.name || "",
          url: route[subItem.url].url || "",
          iconCls: route[subItem.url].iconCls || "",
        }))
      });
      // 一级菜单
      _temp = Object.assign({}, {
        name: item.name || "",
        url: item.url || "",
        iconCls: route[item.name].iconCls || "",
        children: subItems.slice(0)
      });
      _nav.push(_temp);
    }
  }
  state.nav = _nav;
};
3. 后端接口返回内容
{
    "status": 200,
    "info": "数据查询成功!",
    "param": {
        "nav": {
            "1": {
                "name": "系统管理",
                "url": "",
                "children": [
                    {
                        "name": "组织结构",
                        "url": "Pmsadmin/Oragnize/list"
                    },
                    {
                        "name": "人员管理",
                        "url": "Pmsadmin/Admin/list"
                    },
                    {
                        "name": "角色管理",
                        "url": "Pmsadmin/Role/list"
                    },
                    {
                        "name": "日志管理",
                        "url": "Pmsadmin/Log/record"
                    }
                ]
            },
            "61": {
                "name": "广告位",
                "url": "",
                "children": [
                    {
                        "name": "广告位列表",
                        "url": "Pmsadmin/Place/list"
                    }
                ]
            }
        },
        "auth": [
            "系统管理",
            "Pmsadmin/Oragnize/list",
            "Pmsadmin/Admin/list",
            "Pmsadmin/Role/list",
            "Pmsadmin/Log/record",
            "广告位",
            "Pmsadmin/Place/list"
        ]
    }
}
存在的问题

新增 修改 删除 按钮还无法实现根据用户权限控制其显示

代码上还存在着不足,期待大神能够有更优的解决方案。

前端代码

可参考:https://github.com/kfw001/adm...

如果有更好的想法和建议,欢迎评论。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91765.html

相关文章

  • 权限设计杂谈

    摘要:权限设计的杂谈这篇文章的定位,不是宣传某个框架,仅仅之是梳理一下有关权限方面的一些想法和最近项目中的一些探索过程。而这两者的取舍则是有设计人员决定的。数据抽象原则最小特权划分从某个程度上来说决定了控制的对象,而数据抽象原则是是决定了操作。 权限设计的杂谈 这篇文章的定位,不是宣传某个框架,仅仅之是梳理一下有关权限方面的一些想法和最近项目中的一些探索过程。我们主要想解决一下问题。 什么...

    yck 评论0 收藏0
  • RBAC笔记

    摘要:例如,系统中某个用户辞职了,只需要将系统中该用户的角色授权撤销即可。 Q0.有哪些概念需要知道? 一些概念的具体定义如下 用户(user): 和计算机系统交互的人(在许多设计方案中,单个用户可能拥有多个登录标识(ID),这些标识可能同时处于活跃状态,但身份验证机制可以使多个标识匹配到某个具体的人,即用户对于计算机系统来说具有唯一性) 主体(subject): 一个代表用户行为的计算机...

    ZweiZhao 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<