后台系统权限控制太重要了,Vue3高级写法完美实现,一篇文章包含路由守卫和自定义指令等等,看了绝不后悔

在后台管理系统中,权限控制是必不可少的,那么权限控制都包括什么呢?

  • 页面权限:菜单栏是否显示
  • 功能权限:按钮是否可点击

不同的账号显示的菜单栏不同,页面上可以操作的按钮也不一样,这背后就是依据RBAC模型来实现的

什么是RBAC

RBAC(Role-Based Access Control)模型是一种用于访问控制的权限管理模型。在 RBAC 模型中,权限的分配和管理是基于角色进行的。
RBAC 模型包含以下几个核心概念:

  1. 用户(User):用户是实际使用系统的人员或实体。每个用户都可以关联到一个或多个角色。
  2. 角色(Role):角色代表了一组具有相似权限需求的用户。每个用户可以被分配一个或多个角色,并通过角色来确定其拥有的权限。
  3. 权限(Permission):权限指定了对系统资源进行操作的能力。它们定义了用户在系统中可以执行的动作或访问的资源范围。系统中的菜单、接口、按钮都可以抽象为资源。

在 RBAC 模型中,管理员为每个角色分配适当的权限,然后将角色与用户关联起来,从而控制用户对系统资源的访问。这种角色与权限之间的层次结构和关系,使得权限管理更加灵活和可维护。如下图:
image.png

辅助业务

  1. 员工管理(用户列表)
    • 查看详情
    • 为用户分配角色
    • 删除用户操作
  2. 角色列表:
    • 为角色分配权限
  3. 权限列表

实现逻辑

  1. 页面权限的核心是 路由表配置,路由表分为两部分:
    • 共有路由表(publicRoutes):每个角色都有的路由表,例如登录界面、404界面、401界面
    • 私有路由表(privateRoutes):不同角色拥有不同的路由表

整个 页面权限 实现分为以下几步:

  1. 获取 权限数据

  2. 私有路由表 不再被直接加入到 routes

  3. 利用 addRoute API 动态添加路由到 路由表

  4. 功能权限的核心在于 根据数据隐藏功能按钮,隐藏的方式可以通过Vue的自定义指令进行控制

整个 功能权限 实现分为以下几步:

  1. 获取 权限数据
  2. 定义 隐藏按钮方式(通过指令)
  3. 依据数据隐藏按钮

页面权限代码实现

获取权限数据

在store中封装获取数据的方法,并存储用户数据,在src/permission中调用方法(在后面)
权限数据在 **userInfo -> permission -> menus**** 中**

import { getUserInfo } from '@/api/sys'

export default {
  namespaced: true,
  state: () => ({
    userInfo: {}
  }),
  mutations: {
    ...
    setUserInfo(state, userInfo) {
      state.userInfo = userInfo
    }
  },
  actions: {
    ...
    async getUserInfo(context) {
      const res = await getUserInfo()
      this.commit('user/setUserInfo', res)
      return res
    },
		...
  }
}

接口返回数据如下图:
image.png

私有路由表 不再被直接加入到 routes

/**
 * 私有路由表
 */
export const privateRoutes = []

/**
 * 公开路由表
 */
export var publicRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index')
  },
  {
    path: '/',
    component: layout,
    redirect: '/profile',
    children: [
      {
        path: '/profile',
        name: 'profile',
        component: () => import('@/views/profile/index'),
        meta: {
          title: 'profile',
          icon: 'personnel'
        }
      },
      {
        path: '/404',
        name: '404',
        component: () => import('@/views/error-page/404')
      },
      {
        path: '/401',
        name: '401',
        component: () => import('@/views/error-page/401')
      }
    ]
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes: publicRoutes
})

利用 addRoute API 动态添加路由到 路由表 中

  1. 创建 router/modules 文件夹,放入所有权限路由

为每个权限路由指定一个 name,每个 name 对应一个 页面权限name值是用来和获取到的数据进行匹配的
例如:RoleList.js,其余的不在此展示了

import layout from '@/layout'

export default {
  path: '/user',
  component: layout,
  redirect: '/user/manage',
  
  name: 'roleList',
  
  meta: {
    title: 'user',
    icon: 'personnel'
  },
  children: [
    {
      path: '/user/role',
      component: () => import('@/views/role-list/index'),
      meta: {
        title: 'roleList',
        icon: 'role'
      }
    }
  ]
}
  1. router/index 中合并这些路由到 privateRoutes
import ArticleCreaterRouter from './modules/ArticleCreate'
import ArticleRouter from './modules/Article'
import PermissionListRouter from './modules/PermissionList'
import RoleListRouter from './modules/RoleList'
import UserManageRouter from './modules/UserManage'

export const asyncRoutes = [
  RoleListRouter,
  UserManageRouter,
  PermissionListRouter,
  ArticleCreaterRouter,
  ArticleRouter
]
  1. 创建 store/modules/permission 模块,专门处理路由
// 专门处理权限路由的模块
import { publicRoutes, privateRoutes } from '@/router'
export default {
  namespaced: true,
  state: {
    // 路由表:初始拥有静态路由权限
    routes: publicRoutes
  },
  mutations: {
    /**
     * 增加路由
     */
    setRoutes(state, newRoutes) {
      // 永远在静态路由的基础上增加新路由
      state.routes = [...publicRoutes, ...newRoutes]
    }
  },
  actions: {
    /**
     * 根据权限筛选路由
     */
    filterRoutes(context, menus) {
      const routes = []
      // 路由权限匹配
      menus.forEach(key => {
        // 权限名 与 路由的 name 匹配
        routes.push(...privateRoutes.filter(item => item.name === key))
      })
      // 最后添加 不匹配路由进入 404
      routes.push({
        path: '/:catchAll(.*)',
        redirect: '/404'
      })
      context.commit('setRoutes', routes)
      return routes
    }
  }
}
  1. src/permission 中,触发store中的getUserInfo获取用户数据,然后触发filterRoutes将匹配到的数据使用addRoute添加到路由表中
import router from './router'
import store from './store'

const whiteList = ['/login']

/**
 * 路由前置守卫
 * to 要去哪里
 * from 当前导航正要离开的路由
 * next 往哪去
 */
router.beforeEach(async (to, from, next) => {
  if (store.getters.token) {
    if (to.path === '/login') {
      next('/')
    } else {
      // 判断用户信息是否获取
      // 若不存在用户信息,则需要获取用户信息
      if (!store.getters.hasUserInfo) {
        const { permission } = await store.dispatch('user/getUserInfo')
        // 处理用户权限,筛选出需要添加的权限
        const filterRoutes = await store.dispatch(
          'permission/filterRoutes',
          permission.menus
        )
        // 利用 addRoute 循环添加
        filterRoutes.forEach(item => {
          router.addRoute(item)
        })
        // 添加完动态路由之后,需要在进行一次主动跳转
        return next(to.path)
      }
      next()
    }
  } else {
    if (whiteList.indexOf(to.path) > -1) {
      next()
    } else {
      next('/login')
    }
  }
})

到这里页面权限的动态路由就完成了,但是更换用户后需要刷新页面,左侧菜单才会更新,原因就是:退出登录时,添加的路由表并未被删除

要解决这个问题,我们只需要在退出登录时,删除动态添加的路由表即可
(1)在 router/index 中定义 resetRouter 方法,使用removeRouteAPI 移除路由

/**
 * 初始化路由表
 */
export function resetRouter() {
  if (
    store.getters.userInfo &&
    store.getters.userInfo.permission &&
    store.getters.userInfo.permission.menus
  ) {
    const menus = store.getters.userInfo.permission.menus
    menus.forEach((menu) => {
      router.removeRoute(menu)
    })
  }

(2)在退出登录的动作下,触发该方法

功能权限代码实现

对于功能权限,我这里是通过这样格式的指令进行控制 v-permission="['importUser']"

  1. 获取权限数据在页面权限第一步已经完成,就是**userInfo -> permission -> points**
  2. 创建自定义指令 directives/permission
import store from '@/store'

function checkPermission(el, binding) {
  // 获取绑定的值,此处为权限
  const { value } = binding
  // 获取所有的功能指令
  const points = store.getters.userInfo.permission.points
  // 当传入的指令集为数组时
  if (value && value instanceof Array) {
    // 匹配对应的指令
    const hasPermission = points.some(point => {
      return value.includes(point)
    })
    // 如果无法匹配,则表示当前用户无该指令,那么删除对应的功能按钮
    if (!hasPermission) {
      el.parentNode && el.parentNode.removeChild(el)
    }
  } else {
    // eslint-disabled-next-line
    throw new Error('v-permission value is ["admin","editor"]')
  }
}

export default {
  // 在绑定元素的父组件被挂载后调用
  mounted(el, binding) {
    checkPermission(el, binding)
  },
  // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
  update(el, binding) {
    checkPermission(el, binding)
  }
}
  1. directives/index 中绑定该指令
import permission from './permission'
export default app => {
  app.directive('permission', permission)
}

不要忘了在main.js中引入呦

import installDirective from '@/directives'
...
installDirective(app)
  1. 在对应的按钮上使用指令即可,如下:
<el-button
    ...
    v-permission="['distributeRole']"
>分配角色</el-button>

将自定义指令方法的参数打印出来
image.png
至此我们的权限控制就完成了,如有什么不足的地方,欢迎大家评论区留言。

  • 24
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
【资源说明】 课程设计基于Vue3和Django实现的rbac权限管理系统python源码(含前端)+项目说明.zip 在线预览 [https://xadmin.dvcloud.xin/](https://xadmin.dvcloud.xin/) 账号密码:admin/admin123 生成数据表并迁移 ```shell python manage.py makemigrations python manage.py migrate ``` 创建管理员账户 ```shell python manage.py createsuperuser ``` 启动程序 ## a.本地环境直接启动 ```shell python manage.py start all ``` ## b.容器化启动 ```shell docker compose up -d ``` 导入默认菜单 ```shell python manage.py loaddata loadjson/menu.json ``` # 附录 容器部署 ```shell docker compose up ``` 保存当前菜单为文件 ```shell python manage.py dumpdata system.MenuMeta system.Menu -o loadjson/menu.json ``` nginx 前端代理 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载,沟通交流,互相学习,共同进步!
Vue2中,我们可以使用全局的路由守卫控制路由的跳转和访问权限Vue3中的路由守卫也有所改变。 首先,Vue2中的全局路由守卫包括了beforeEach、beforeResolve和afterEach等钩子函数,这些钩子函数分别在路由跳转之前、解析完组件之前以及路由跳转完成之后触发。而在Vue3中,路由守卫写法更加灵活和精简,使用了新的Composition API中的onBeforeRouteLeave、onBeforeRouteUpdate和onBeforeRouteEnter等函数来替代全局路由守卫。 其次,Vue2中的路由守卫是通过传入next函数来进行路由的跳转控制,可以通过调用next函数传入一个新的路由路径来进行跳转,也可以通过调用next(false)来阻止路由的跳转。而在Vue3中,路由守卫使用了Promise的方式来控制路由的跳转。可以通过返回一个Promise对象来决定路由是否跳转或者延迟路由的跳转。 此外,Vue2中的路由守卫是基于全局的,即对所有路由都生效。而在Vue3中,路由守卫可以局部应用于具体的路由或者路由组件,在组合式API中可以通过在组件内部使用setup函数来定义特定路由的守卫。 综上所述,Vue3中的路由守卫相比Vue2有了一些改变,使用了新的Composition API和Promise的方式来实现。同时,Vue3中的路由守卫更加灵活和精简,可以更方便地控制路由的跳转和访问权限。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [vue2和vue3的区别(由浅入深)](https://blog.csdn.net/weixin_42974827/article/details/126560362)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值