概括
权限管理在后端项目中主要体现在对接口访问权限的控制,在前端项目中主要体现在对菜单访问权限的控制。今天就结合项目来讲解一下Vue中的权限控制。
技术栈
实现菜单的动态权限控制,要使用到以下两种技术: Vue.router 和 Vuex,要想实现效果那必须要掌握这两个方法,下面介绍以下这两种方法。
Vue.router
Vue Router 是 Vue.js 官方提供的路由管理器,它和 Vue.js 具有相同的作者,可以轻松地实现单页应用程序(SPA)中的视图导航和数据传递。Vue Router 可以帮助我们更好地组织应用程序的代码结构和视图布局,提高应用程序的可维护性和可扩展性。
简单来说路由就是一个路径,可以实现从一个页面跳转到另一个页面,就比如你浏览网页时,没点开一个网页页面就会进行跳转,这些就是通过路由的方式来实现的。
在后台管理系统当中侧边菜单一般都是要用路由来实现的,要想实现动态菜单显示,就只需要动态的配置路由即可。
使用 Vue Router 需要经过以下几个步骤:
-
安装:通过 npm 或者 yarn 安装 Vue Router。
-
引入和注册:在 Vue.js 项目中引入 Vue Router 并将其注册为一个插件。
-
配置路由:定义路由映射关系,即 URL 和组件之间的对应关系。
-
渲染视图:在 Vue.js 组件中使用 router-view 标签渲染动态路由对应的组件。
-
添加导航链接:在 Vue.js 组件中使用 router-link 标签添加导航链接,可以通过点击链接进行页面跳转和参数传递。
Vuex
Vue是 Vue.js 官方提供的状态管理工具,它可以帮助我们更好地管理全局状态(例如用户信息、
页面状态、列表数据等),并实现组件之间的衔接和数据共享。
Vuex简单理解为一个全局的状态管理器,我们可以把一些全局的状态存储在里面。当我们在
多个组件中显示这些状态时,只要在任意一个组件中改变这个状态,基于Vue的响应式渲染,其余
组件中的这个状态均会改变。
Vuex 的核心概念包括:
State:应用程序的状态存储中心,所有组件共享该状态。
Getters:派生出一些衍生状态,例如计算数据、过滤数据等。
Mutations:修改 State 中的状态,必须是同步操作。
Actions:异步操作或批量操作,可以包含多个 mutations,提交到 mutations 中。
Modules:将大型的 state 分割成多个小的模块,便于维护。
核心流程:
RBAC
RBAC(Role-Based Access Control)是基于角色的访问控制模型,是一种广泛应用于信息安全领域的访问控制机制。在 RBAC 模型中,访问权限被分配给了不同的角色,而用户则被分配到不同的角色上,从而实现了对用户访问资源的控制。
在 RBAC 模型中,用户可以拥有多个角色,每个角色可以访问一组权限,而权限又可以包含多个操作。通过将用户和角色进行分离,RBAC 可以极大地简化系统的管理和维护,同时也可以提高系统的安全性和可扩展性。
在实际应用中,RBAC 通常包括以下几个要素:
用户:需要访问某些资源的具体用户。
角色:表示一组相关的权限集合,例如管理员、普通用户等。
权限:表示能够访问或执行某些操作的权利。
对象:表示需要被保护的资源,例如文件、数据库、网络连接等。
命令:表示用户执行的命令或者请求,需要经过鉴权来确定是否有权限执行。
权限设计思想:
一种基于角色的设计思想
- 给员工配置角色 (一个员工可以拥有多个角色)
- 给角色配置权限点 (一个角色可以有多个权限点)
员工只要有了角色之后,就自动拥有了角色绑定的所有权限点,当有了权限就可以实现不同权限登录进来的页面效果和功能是不一样的,这个思想很重要。
菜单的动态权限控制
页面级别
首先要修改 src/router/index.js 中的路由表,将路由表进行拆分,拆分成必须要显示的静态路由表和可以动态显示的动态路由表。
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true,
meta: {
title: '登录'
}
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}]
}
]
export const asyncRouters = [
departmentRouter,
roleRouter,
employeeRouter,
permissionRouter,
salaryRouter,
socialRouter,
attendanceRouter,
approvaleRouter
]
然后我们获取Vuex中的数据。这个数据是在路由守卫中捕获到的用户登录时的信息,包含了用户的权限路径,这个获取的数据是与后端沟通好与路由名字一致这样方便我们实现权限控制。
图1
图2
图1获取的数据是通过vuex调用图2return出来的数据,这些数据是已经获取到用户登录的token令牌后做的操作。
得到路由信息和用户登录后携带的信息后,在路由守卫中要对所有页面的路由信息通过 filter方法 进行筛选。
实现代码:
// 路由守卫中添加的
const resRouters = asyncRouters.filter(item => {
return res.roles.menus.includes(item.children[0].name)
})
详细讲解这一块代码
asyncRouters 是从 src/router/index.js 文件中导出的所有动态路由,上面有介绍。
res.roles.menus 是用户登录时携带的数据,这些数据对应的就是路由的 name。item.children[0].name 是所有路由的 name 这个是自己配置的
在循环里面进行筛选,通过 includes 方法来筛选用户携带的信息是否包含此路由,然后return出去,用个变量来接收筛选结果,方便后面操作。
通过上述我们得到了筛选出来的结果,就要开始动态的渲染路由了。
首先要将筛选结果存到Vuex中,这样渲染的时候方便调用,因为路由成了动态的,在 router 文件中访问不到,所以存到Vuex中。
import { constantRoutes } from '@/router/index'
export const state = {
// 存储的是不需要权限也能访问的页面
menuList: [...constantRoutes]
}
export const mutations = {
updateMenu(state, menu) {
// 将筛选出来的页面通过展开运算符加到 menuList 中
state.menuList = [...constantRoutes, ...menu]
}
}
export default {
namespaced: true,
state,
mutations
}
通过 store.commit('menu/updateMenu', resRouters)来将筛选的数据存放到Vuex中
再在侧边菜单路由渲染的地方调用这个变量,进行循环渲染即可,就可以达到不同的用户看到的侧边菜单不一致了,不过光看到侧边菜单不一致也不行,还没实现动态路由的配置,要通过 addRoutes 方法进行动态路由的配置。
// 动态生成当前的用户能访问的页面
router.addRoutes(resRouters)
// 是在路由守卫里面添加的
这些都做完了,才是真正的做到了动态路由的配置和渲染,做到了页面权限的控制,用户的权限不一样,见到的页面不一致,不过会有 bug。
bug
你会发现你切换一下页面会跳转到 404 页面,这是为什么呢?是因为我们是动态的对其进行添加路由的,初始路由就有四个,其中包括 404 页面,在进行动态添加路由的时候会添加到 404 路由后面,这样你一切换刷新就会跳转到 404 页面 。
如何解决呢?
也很简单,就是直接将 404 单独添加到路由的最后面即可,只需添加一行代码即可
// 单独处理 404 将此页面放到所有路由页面的最后,这样就不会出现刷新页面变 404
resRouters.push({ path: '*', redirect: '/404', hidden: true })
但是这样处理就会出现另一个 bug,就是页面会白屏,这是因为刚刚 addRoutes()
就立刻访问被添加的路由,然而此时 addRoutes()
没有执行结束,因而找不到刚刚被添加的路由导致白屏。因此需要从新访问一次路由才行。
解决: 使用next({ ...to,replace: true
})来确保addRoutes()时动态添加的路由已经被完全加载上去。
再对其进行个小优化,当做完这一切,你会发现当你退出从新登录时路由会再执行一遍,这个就需要退出的时候清空一下路由了。
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
这个方法定义在 src/router/index.js 中,当你在退出的时候调用一下这个方法就可以解决了。
按钮级别权限控制
目标
员工A和员工B都可以访问同一个页面(以员工管理为例),但是员工A可以导出excel,员工B就不可以导出excel(看不到按钮)
思路
用户登陆成功后,用户可以访问的按钮级别权限保存在points数组中。而这个数据我们是保存在vuex中的,所以,就可以在项目的任意地方来中访问。
- 如果某个按钮上的标识在points出现,则可以显示出来
怎么实现呢?
就是给在需要进行权限控制的按钮设置 v-if="从vuex中获取到的points进行筛选判断"
这个方法太繁琐了,因为需要配置权限的按钮比较多,所以不方便,我们这里采用的是封装一个自定义指令 v-allow 来实现,只需调用这个方法里面传一个标识即可。
因为在一个项目中自定义指令的封装还是比较广泛的,所以我们单独开一个文件夹,使用Vue.use在main.js中来调用此指令即可。
自定义指令代码奉上:
import store from '@/store'
export default {
install(Vue) {
// 封装按钮权限自定义指令
Vue.directive('allow', {
inserted: (el, binding) => {
const res = store.state.user.userInfo.roles.points
// console.log(el, binding, res)
if (!res.includes(binding.value)) el.remove()
}
})
}
}
在main.js调用即可
import direction from '@/directive'
Vue.use(direction)
这样就可以实现按钮级别的权限控制,用户的权限不同,使用的功能不同。
我们就实现了后台管理系统的权限控制了,我们前端只能实现不同权限显示的页面不同,真正的权限控制还是后端来控制的,我们是配合后端来实现的权限控制。
到这里完美撒花!!!