每个公司都是自己的后台管理系统,但每个员工的权限是不一样的,如:经理可以进行员工管理、对员工信息进行删除等等...则这里进行权限管理可以根据后台给每个员工返回的数据进行处理。
登录:当用户填写账户密码后台服务器验证通过之后,服务器返回一个token,拿到token存储起来,根据这个token来获取这个员工的数据进行页面的渲染。
权限验证:通过token获取员工对应的权限数据,动态根据员工的权限渲染出对应的路由页面,通过 router.addRoutes 动态挂载这些路由。
RBAC
这里使用到 RBAC 的逻辑来实现。RBAC (基于角色的访问控制) 。在 RBAC 中,有三个组成,用户、角色、权限 。
RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离(区别于ACL模型),极大地方便了权限的管理 :
-
User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
-
Role(角色):不同角色具有不同的权限
-
Permission(权限):访问权限
-
用户-角色映射:用户和角色之间的映射关系
-
角色-权限映射:角色和权限之间的映射
它们之间的关系如下图所示:
第一步:拦截路由
需要另声明一个数组进行路由存储,就是把静态路由改成一个动态路由
// 截断默认路由
export const asyncRoutes = [
approvalsRouter,
departmentsRouter,
employeesRouter,
permissionRouter,
attendancesRouter,
salarysRouter,
settingRouter,
socialRouter
]
// 把本在路由规则上的截取下来
第二步:筛选路由
根据获取员工的数据对路由进行筛选,选出对应的权限的路由,并存储到 vuex 中进行组件之间的共享。
// 在导航守卫进行获取
const menus = store.state.user.userInfo.roles.menus // 员工的权限标识
// 筛选动态路由菜单 -- 进行dispatch操作
const routes = await store.dispatch('permission/getRoutes', menus)
第三步:vuex 进行存储且合并之前路由
// constantRoutes 是原有的路由规则
import { asyncRoutes, constantRoutes } from '@/router'
const state = {
routes: [] // 来渲染路由菜单
}
const mutations = {
setRoutes(state, routes) {
// 存储筛选出来的路由菜单并和之前路由进行合并
state.routes = [
...constantRoutes,
...routes
]
}
}
const actions = {
getRoutes({ commit }, menus) {
// 筛选这个用户所拥有的权限,筛选路由
// 这里的name是路由里的name
const routes = asyncRoutes.filter(item => menus.includes(item.name))
// 进行存储
commit('setRoutes', routes)
// 返回筛选完的路由
return routes
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
第四步:路由菜单渲染
这里使用 了 vue-element-admin 进行开发,所以使用的菜单渲染
// 在 src/layout/components/Sidebar.vue
// 当前的是静态路由
// return this.$router.options.routes
// 使用动态路由进行菜单渲染
return this.$store.state.permission.routes
第五步:添加路由规则
// 现在我们路由菜单已经筛选到了,但我们的路由地址访问不到,由于这个disoatch是promise对象,可以使用await获取到dispatch函数的返回值,再使用addRoutes方法
// 如果在不是首页进行页面刷新则会跑到404页面
// 因为我们筛选出来的路由使用addRoutes是进行拼接,那么404页面就在前面,页面刷新会先找到404页面,所以404页面只能在最后拼接
// 根据上面 const routes = await store.dispatch('permission/getRoutes', menus)
router.addRoutes([
...routes,
{ path: '*', redirect: '/404', hidden: true }
])