全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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

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

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

实现 RBAC 权限管理需要后端接口支持,这里仅提供前端解决方案。

因代码篇幅较大,对代码进行了删减,文中 “...” 即为省略的一部分代码。

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

1. 路由与侧边菜单分离

侧边菜单相关代码 Main.vue

<template>
<!-- ... -->
  <aside :class="collapsed?'menu-collapsed':'menu-expanded'">
    <!--导航菜单-->
    <el-menu :default-active="$route.path"
         class="el-menu-vertical-aliyun" 
         @open="handleopen"
         @close="handleclose"
         @select="handleselect"
         :collapse="collapsed"
         unique-opened router>
      <template v-for="(item,index) in nav">
        <!-- 二级菜单 -->
        <el-submenu :index="index+''"
              v-if="item.children && item.children.length > 0">
          <!-- 二级菜单顶级 -->
          <template slot="title">
            <i :class="['icon',item.iconCls]"></i>
            <span slot="title">{{item.name}}</span>
          </template>
          <!-- 二级菜单下级 -->
          <el-menu-item-group>
            <!--<span slot="title">{{item.name}}</span>-->
            <!-- && child.url-->
            <template v-for="child in item.children">
              <!--无三级菜单-->
              <el-menu-item
                  :index="child.url"
                  :key="child.url"
                  v-if="!child.children">
                {{child.name}}
              </el-menu-item>
              <!--有三级菜单-->
              <el-submenu
                  :index="child.url"
                  :key="child.url"
                  v-if="child.children">
                <span slot="title">{{child.name}}</span>
                <el-menu-item v-for="subChild in child.children"
                       :index="subChild.url"
                       :key="subChild.url">
                  {{subChild.name}}
                </el-menu-item>
              </el-submenu>
            </template>
          </el-menu-item-group>
        </el-submenu>
        <!-- 一级菜单 -->
        <el-menu-item v-if="!item.children"
               :index="item.url">
          <i :class="['icon',item.iconCls]"></i>
          <span slot="title">{{item.name}}</span>
        </el-menu-item>
  
      </template>
    </el-menu>
  </aside>
<!-- ... -->
</template>

<script>
  export default {
    // ...
    computed: {
     // 从 Vuex 中获取导航菜单
     nav() {
      return this.$store.state.nav;
     }
    }
    // ...
  }
</script>

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"
    ]
  }
}

存在的问题

  • 新增 修改 删除 按钮还无法实现根据用户权限控制其显示
  • 代码上还存在着不足,期待大神能够有更优的解决方案。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# vuejs  # 权限管理  # Vue.js  # RBAC角色权限管理  # RBAC角色权限  # vue2模拟vue-element-admin手写角色权限的实现  # vue router权限管理实现不同角色显示不同路由  # vue3使用element-plus搭建后台管理系统之菜单管理功能  # 详解vue后台系统登录态管理  # vue后台系统管理项目之角色权限分配管理功能(示例详解)  # 跳转  # 后端  # 不存在  # 组织结构  # 将其  # 大神  # 采用了  # 项目管理  # 上还  # 即为  # 大家多多  # 进行了  # 到该  # 物业管理  # 判断是否  # 数据查询  # 无法访问  # 加载  # 列表中  # export 


相关文章: 为什么Go需要go mod文件_Go go mod文件作用说明  建站主机如何安装配置?新手必看操作指南  香港服务器部署网站为何提示未备案?  胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?  高端建站如何打造兼具美学与转化的品牌官网?  电脑免费海报制作网站推荐,招聘海报哪个网站多?  齐河建站公司:营销型网站建设与SEO优化双核驱动策略  建站主机选哪种环境更利于SEO优化?  小自动建站系统:AI智能生成+拖拽模板,多端适配一键搭建  ,网站推广常用方法?  如何做网站制作流程,*游戏网站怎么搭建?  魔毅自助建站系统:模板定制与SEO优化一键生成指南  如何在阿里云完成域名注册与建站?  浙江网站制作公司有哪些,浙江栢塑信息技术有限公司定制网站做的怎么样?  php能控制zigbee模块吗_php通过串口与cc2530 zigbee通信【介绍】  如何确认建站备案号应放置的具体位置?  建站之星如何取消后台验证码生成?  如何生成腾讯云建站专用兑换码?  c# await 一个已经完成的Task会发生什么  如何通过主机屋免费建站教程十分钟搭建网站?  专业网站制作企业网站,如何制作一个企业网站,建设网站的基本步骤有哪些?  盘锦网站制作公司,盘锦大洼有多少5G网站?  建站之星logo尺寸如何设置最合适?  宁波免费建站如何选择可靠模板与平台?  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  C++用Dijkstra(迪杰斯特拉)算法求最短路径  在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?  如何获取免费开源的自助建站系统源码?  如何快速搭建安全的FTP站点?  如何通过宝塔面板实现本地网站访问?  Android使用GridView实现日历的简单功能  如何选择高效稳定的ISP建站解决方案?  如何用免费手机建站系统零基础打造专业网站?  企业微网站怎么做,公司网站和公众号有什么区别?  制作宣传网站的软件,小红书可以宣传网站吗?  如何快速生成凡客建站的专业级图册?  如何通过虚拟主机快速完成网站搭建?  高防服务器如何保障网站安全无虞?  香港服务器租用费用高吗?如何避免常见误区?  如何快速搭建虚拟主机网站?新手必看指南  详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)  c# Task.Yield 的作用是什么 它和Task.Delay(1)有区别吗  公司网站的制作公司,企业网站制作基本流程有哪些?  专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?  整人网站在线制作软件,整蛊网站退不出去必须要打我是白痴才能出去?  微网站制作教程,不会写代码,不会编程,怎么样建自己的网站?  Python文件管理规范_工程实践说明【指导】  如何通过VPS搭建网站快速盈利?  电商网站制作价格怎么算,网上拍卖流程以及规则?  济南网站建设制作公司,室内设计网站一般都有哪些功能? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。