RBAC权限的设计

一、设计思想

传统模式下的权限设计(每个人进行单独的权限设置)

在这里插入图片描述

RBAC的权限模型

  • RBAC(Role-Based Access control) 基于角色权限分配

在这里插入图片描述

  • RBAC实现了用户和权限点的分离,想对某个用户设置权限,只需要对该用户设置相应的角色即可,而该角色就拥有了对应的权限,这样一来,权限的分配和设计就做到了极简,高效,当想对用户收回权限时,只需要收回角色即可

二、给员工分配角色

用户和角色是一对多的关系

在这里插入图片描述

在这里插入图片描述

三、权限点管理

在企业服务中,权限一般分割为 页面访问权限,按钮操作权限,API访问权限

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、给角色分配权限

在这里插入图片描述
在这里插入图片描述

五、前端权限的应用(页面访问和菜单)

5.1 权限受控的主体思想

上边已经把给用户分配了角色, 给角色分配了权限,那么在用户登录获取资料的时候,会自动查出该用户拥有哪些权限,这个权限需要和我们的菜单还有路由有效结合起来

-在权限管理页面中,我们设置了一个标识, 这个标识可以和我们的路由模块进行关联,也就是说,如果用户拥有这个标识,那么用户就可以拥有这个路由模块,如果没有这个标识,就不能访问路由模块

在这里插入图片描述

vue-router提供了一个叫做addRoutes的API方法,这个方法的含义是动态添加路由规则

在这里插入图片描述

5.2 新建Vuex中管理权限的模块

用户资料存到vuex中,其内部有一个权限数据

-1.新增一个permission模块 src/store/modules/permission.js

// vuex的权限模块
import { constantRoutes } from '@/router'
// vuex中的permission模块用来存放当前的 静态路由 + 当前用户的 权限路由
const state = {
  routes: constantRoutes // 所有人默认拥有静态路由
}
const mutations = {
  // newRoutes可以认为是 用户登录 通过权限所得到的动态路由的部分
  setRoutes(state, newRoutes) {
    // 下面这么写不对 不是语法不对 是业务不对
    // state.routes = [...state.routes, ...newRoutes]
    // 有一种情况  张三 登录 获取了动态路由 追加到路由上  李四登录 4个动态路由
    // 应该是每次更新 都应该在静态路由的基础上进行追加
    state.routes = [...constantRoutes, ...newRoutes]
  }
}
const actions = {}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

  • 2.在Vuex管理模块index.js中引入permisson模块
import permission from './modules/permission'
	
const store = new Vuex.Store({
  modules: {
    // 子模块   $store.state.app.
    // mapGetters([])
    app,
    settings,
    user,
    permission
  },
  getters
})

5.3 Vuex筛选权限路由

在这里插入图片描述

  • vuex中的用户信息下的权限信息

在这里插入图片描述

  • 将路由模块的根节点name属性命名和权限标识一致,这样只要标识能对上,就说明用户拥有了该权限

在这里插入图片描述

  • vuex的permission中提供一个action,进行关联
import { asyncRoutes, constantRoutes } from '@/router'

const actions = {
  // 筛选权限路由
  // 第二个参数为当前用户的所拥有的菜单权限
  // menus: ["settings","permissions"]
  // asyncRoutes是所有的动态路由
  // asyncRoutes  [{path: 'setting',name: 'setting'},{}]
  filterRoutes(context, menus) {
    const routes = []
    //   筛选出 动态路由中和menus中能够对上的路由
    menus.forEach(key => {
      // key就是标识
      // asyncRoutes 找 有没有对象中的name属性等于 key的 如果找不到就没权限 如果找到了 要筛选出来
      routes.push(...asyncRoutes.filter(item => item.name === key)) // 得到一个数组 有可能 有元素 也有可能是空数组
    })
    // 得到的routes是所有模块中满足权限要求的路由数组
    // routes就是当前用户所拥有的 动态路由的权限
    context.commit('setRoutes', routes) // 将动态路由提交给mutations
    return routes // 这里为什么还要return  state数据 是用来 显示左侧菜单用的  return  是给路由addRoutes用的
  }
}

5.4 权限拦截处调用筛选权限Action

// 权限拦截在路由跳转  导航守卫

import router from '@/router'
import store from '@/store' // 引入store实例 和组件中的this.$store是一回事
import nprogress from 'nprogress' // 引入进度条
import 'nprogress/nprogress.css' // 引入进度条样式
// 不需要导出  因为只需要让代码执行即可
// 前置守卫
// next是前置守卫必须必须必须执行的钩子  next必须执行 如果不执行 页面就死了
// next()  放过
// next(false) 跳转终止
// next(地址) 跳转到某个地址
const whiteList = ['/login', '/404'] // 定义白名单
router.beforeEach(async(to, from, next) => {
  nprogress.start() // 开启进度条的意思
  if (store.getters.token) {
    // 只有有token的情况下 才能获取资料
    //   如果有token
    if (to.path === '/login') {
      // 如果要访问的是 登录页
      next('/') // 跳到主页  // 有token 用处理吗?不用
    } else {
      // 只有放过的时候才去获取用户资料
      // 是每次都获取吗
      // 如果当前vuex中有用户的资料的id 表示 已经有资料了 不需要获取了 如果没有id才需要获取
      if (!store.getters.userId) {
        // 如果没有id才表示当前用户资料没有获取过
        // async 函数所return的内容 用 await就可以接收到
        const { roles } = await store.dispatch('user/getUserInfo')
        // 如果说后续 需要根据用户资料获取数据的话 这里必须改成 同步
        // 筛选用户的可用路由
        // actions中函数 默认是Promise对象 调用这个对象 想要获取返回的值话 必须 加 await或者是then
        // actions是做异步操作的
        const routes = await store.dispatch('permission/filterRoutes', roles.menus)
        // routes就是筛选得到的动态路由
        // 动态路由 添加到 路由表中 默认的路由表 只有静态路由 没有动态路由
        // addRoutes  必须 用 next(地址) 不能用next()
        router.addRoutes(routes) // 添加动态路由到路由表  铺路
        // 添加完动态路由之后
        next(to.path) // 相当于跳到对应的地址  相当于多做一次跳转 为什么要多做一次跳转
        // 进门了,但是进门之后我要去的地方的路还没有铺好,直接走,掉坑里,多做一次跳转,再从门外往里进一次,跳转之前 把路铺好,再次进来的时候,路就铺好了
      } else {
        next()
      }
    }
  } else {
    //   没有token的情况下
    if (whiteList.indexOf(to.path) > -1) {
      //  表示要去的地址在白名单
      next()
    } else {
      next('/login')
    }
  }
  nprogress.done() // 解决手动切换地址时 进度条不关闭的问题
})
// 后置守卫
router.afterEach(() => {
  nprogress.done() // 关闭进度条
})

5.5 静态路由动态路由解除合并

src/router/index.js下

在这里插入图片描述

5.6 显示左侧菜单内容

此时我们发现左侧菜单失去了内容,这是因为左侧菜单读取的是固定的路由,我们要把它换成实时的最新路由

  • 在src/store/getters.js配置导出routes
const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  name: state => state.user.userInfo.username, // 建立用户名称的映射
  userId: state => state.user.userInfo.userId, // 建立用户id的映射
  companyId: state => state.user.userInfo.companyId, // 建立用户的公司Id映射
  routes: state => state.permission.routes // 导出当前的路由
}
export default getters

  • 左侧菜单组件引入 routes路由
 ...mapGetters([
      'sidebar', 'routes'
    ]),

六、退出时,重置路由权限和 404问题

  • 定义重置路由方法 router/index.js
// 重置路由
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
  • 退出时 src\store\modules\user.js
import { resetRouter } from '@/router' 


// 登出的action
  logout(context) {
    // 删除token
    context.commit('removeToken') // 不仅仅删除了vuex中的 还删除了缓存中的
    // 删除用户资料
    context.commit('removeUserInfo') // 删除用户信息
    // 重置路由
    resetRouter()
    // 还有一步  vuex中的数据是不是还在
    // 要清空permission模块下的state数据
    // vuex中 user子模块  permission子模块
    // 子模块调用子模块的action  默认情况下 子模块的context是子模块的
    // 父模块 调用 子模块的action
    context.commit('permission/setRoutes', [], { root: true })
    // 子模块调用子模块的action 可以 将 commit的第三个参数 设置成  { root: true } 就表示当前的context不是子模块了 而是父模块
  }
  • 页面刷新的时候,本来应该拥有权限的页面出现了404,这是因为404的匹配权限放在了静态路由,而动态路由在没有addRoutes之前,找不到对应的地址,就会显示404,所以我们需要将404放置到动态路由的最后
  • src/permission.js
router.addRoutes([...routes, { path: '*', redirect: '/404', hidden: true }]) // 添加到路由表

七、功能权限应用

当我们拥有了一个模块,一个页面的访问权限之后,页面中的某些功能,用户可能有,也可能没有,这就是功能权限

-在员工管理的权限点下, 新增一个删除权限点,启用
在这里插入图片描述

  • 看此用户是否拥有point-user-delete这个point,有就可以让删除能用,没有就隐藏或者禁用

使用Mixin技术将检查方法注入

-新建 src/mixin/checkPermission.js

import store from '@/store'
export default {
  methods: {
    checkPermission(key) {
      const { userInfo } = store.state.user
      if (userInfo.roles && userInfo.roles.points) {
        return userInfo.roles.points.some(item => item === key)
      }
      return false
    }
  }
}

  • 在mian.js文件中统一的注册
import checkPermission from '@/mixin/checkPermission'

Vue.mixin(checkPermission) // 表示所有的组件都拥有了检查的方法
  • 在员工组件中检查权限点
 <el-button :disabled="!checkPermission('POINT-USER-UPDATE')" type="text" size="small" @click="$router.push(`/employees/detail/${obj.row.id}`)">查看</el-button>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gik99

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值