前言
目录
一、将权限应用到页面访问和菜单
1.1 权限控制的实现思路
目前为止,我们已经给用户分配了角色, 给角色分配了权限,那么在用户登录获取资料的时候,我们要自动查出该用户拥有哪些权限,根据用户的权限来控制用户访问路由和菜单显示。
在权限管理页面中,我们设置了一个标识, 这个标识可以和我们的路由模块进行关联,也就是说,如果用户拥有这个标识,那么用户就可以访问这个路由模块,如果没有这个标识,就不能访问路由模块。
最终的权限控制流程如下图所示:
1.2 在Vuex中新建管理权限的模块
在src/store/modules/permission.js中添加如下代码:
// vuex的权限模块
import {
constantRoutes } from '@/router'
// vuex中的permission模块用来存放当前的 静态路由 + 当前用户的 权限路由
const state = {
routes: constantRoutes // 所有人默认拥有静态路由
}
const mutations = {
// newRoutes可以认为是 用户登录 通过权限所得到的动态路由的部分
setRoutes(state, newRoutes) {
// 每次更新在静态路由的基础上进行追加
state.routes = [...constantRoutes, ...newRoutes]
}
}
const actions = {
}
export default {
namespaced: true,
state,
mutations,
actions
}
在src/stroe/index.js中引入permisson模块:
import permission from './modules/permission'
const store = new Vuex.Store({
modules: {
app,
settings,
user,
permission
},
getters
})
1.3 在Vuex中定义筛选权限路由方法
在src/store/modules/permission.js中定义一个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用的
}
1.4 在访问权限拦截处调用筛选权限Action
在src/permission.js中添加如下代码:
// 权限拦截在路由跳转 导航守卫
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的情况下 才能获取资料
if (to.path === '/login') {
// 如果要访问的是登录页,就跳到主页
next('/')
} else {
// 如果当前vuex中有用户的资料的id 表示 已经有资料了 不需要获取了 如果没有id才需要获取
if (!store.getters.userId) {
// 如果没有id才表示当前用户资料没有获取过
const {
roles } = await store.dispatch('user/getUserInfo')
// 筛选用户的可用路由
const routes = await store.dispatch('permission/filterRoutes', roles.menus)
// routes就是筛选得到的动态路由
// 添加动态路由到路由表 铺路
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() // 关闭进度条
})
1.5 静态路由和动态路由解除合并
在src/router/index.js中修改如下代码:
const createRouter = () => new Router({
scrollBehavior: () => ({
y: 0 }),
routes: [...constantRoutes] // 改成只有静态路由
})
1.6 修改左侧菜单读取路由方式
首先在src/store/getters.js中配置导出routes:
const getters = {
routes: state => state.permission.routes // 导出当前的路由
}
export default getters
然后在src\layout\components\Sidebar\index.vue中修改读取动态路由:
computed: {
...mapGetters([
'sidebar', 'routes'
]),
二、退出登录时重置路由权限
我们在router/index.js中,发现项目自带了一个重置路由方法:
// 重置路由
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
所以我们只需要在退出登录时,调用这个方法即可
在src\store\modules\user.js中修改如下代码:
// 登出操作
logout(context) {
// 删除token
context.commit('removeToken')
// 删除用户资料
context.commit('removeUseInfo')
// 重置路由
resetRouter() // 重置路由
// 去设置权限模块下路由为初始状态
// Vuex子模块怎么调用子模块的action 都没加锁的情况下 可以随意调用
// 不加命名空间的情况下的 所有的mutations和action都是挂在全局上的 所以可以直接调用
// 但是加了命名空间的子模块 怎么调用另一个加了命名空间的子模块的mutations
// 加了命名空间的context指的不是全局的context
// mutations名称 载荷 payload 第三个参数 { root: true } 调用根级的mutations或者action
context.commit('permission/setRoutes', [], {
root: true })
}
除此之外,我们发现在页面刷新的时候,本来应该拥有权限的页面出现了404
这是因为我们没有将404放置到动态路由的最后
在src/permission.js中添加如下代码:
router.addRoutes([...routes, {
path: '*', redirect: '/404', hidden: true }]) // 添加到路由表
三、使用Mixin技术实现功能权限应用
我们可以采用一个新的技术mixin(混入)来让所有的组件可以拥有一个公共的方法
在src/mixin/checkPermission.js中添加如下代码:
import store from '@/store'
// 做一个混入对象
export default {
// 混入对象是组件的选项对象
methods: {
// 检查权限 要么有 没有没有 key就是要检查的点
checkPermission(key) {
// 去用户的信息里面找 points中有没有key 如果有key 则认为有权限 如果没有key则认为不能点击
// store.state.user.userInfo.roles.points
const {
userInfo } = store.state.user
if (userInfo.roles && userInfo.roles.points) {
return userInfo.roles.points.some(item => item === key)
}
return false // 如果没进去 说明没权限
}
}
}
在员工组件中检查权限点
<el-button :disabled="!checkPermission('POINT-USER-UPDATE')" type="text" size="small" @click="$router.push(`/employees/detail/${obj.row.id}`)">查看</el-button>