第一次做项目的权限问题时,也是第一次做,主要参考了花裤衩大神的手摸手博文以及百度的一些,综合自己的情况做出来了,实属不易,第一次做,下面正式开始
首先后台会给你一个权限列表的json数组,他也许会放在个人信息接口,也可以单独放一个接口,都是一样的,只是我们前端获取的方式不同,我做的这个是分开放的,在api放入你的后台接口,像这样
然后在/src/store/modules/user.js文件调用此接口,该文件存放的是改用户登录,登出,个人信息等的接口,存放在store,方便全局调用
我们下一步就去src/permission.js文件调用此方法(和main.js同级)解释在代码中注释了,不懂得可以看注释
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
// NProgress.configure({ showSpinner: false }) // NProgress Configuration
// 用来控制按钮的显示
export function hasBtnPermission (permission) {
const myBtns = store.getters.buttons
return myBtns.indexOf(permission) > -1
}
const whiteList = ['/login', '404'] // 白名单路由
// 路由处理--登录验证
// beforeEach() 路由守卫,在跳转之前执行
router.beforeEach(async (to, from, next) => {
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
// 开始进度条
// NProgress.start()
// 设置页面标题
document.title = getPageTitle(to.meta.title)
const hasToken = getToken()
// 判断是否登录时,因为页面刷新后内存中还没有token信息,额外从session中判断一次
if (hasToken) {
if (to.path === '/login') {
// 已经登录的,不能跳到登陆页面,跳到首页
next({ path: '/' })
// NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
let userInfo = store.state.user.name
if (!userInfo) {
try {
// if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('user/getInfo') // 获取用户信息
await store.dispatch('user/getMenu').then(res => { // 拉取权限菜单
let roles = res
store.dispatch('permission/GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
// next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,//菜单权限更新完成,重新进一次当前路由
next(store.getters.addRouters[0]) // hack方法 确保addRoutes已完成 ,//菜单权限更新完成,重新进一次第一个路由
router.options.routes = store.getters.routes // 渲染侧边栏需要用到
})
// })
}).catch(err => {
console.log(err)
})
} catch (error) {
// remove token and go to login page to re-login
// await store.dispatch('user/resetToken')
// Message.error(error || 'Has Error')
// next(`/login?redirect=${to.path}`)
// NProgress.done()
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next('/login')
}
}
} else {
if (to.path === '/login') {
next({ name: 'Dashboard' })
} else {
if (hasPermission(to, store.getters.accessMenu)) {
Util.toDefaultPage(store.getters.accessMenu, to, routes, next);
} else {
next({ path: '/403', replace: true })
}
}
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
// NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
// NProgress.done()
})
store.dispatch('user/getInfo')和store.dispatch('user/getMenu')是在上面讲的调用接口的文件的方法, store.dispatch('permission/GenerateRoutes', { roles })这个也是一样的,这个是在/src/store/modules/permission.js里,下面讲这个文件,这个文件最为重要,所有操作都在这里,重点来了!!!!!!!!
import { asyncRoutes, constantRoutes } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
* roles 是用户的权限表
* route 就是对应的路由配置中的路由对象
*/
// 原来的方法
// function hasPermission(roles, route) {
// if (route.meta && route.meta.roles) {
// return roles.some(role => route.meta.roles.includes(role))
// } else {
// return true
// }
// }
// 根据自己的情况做判断的方法
function hasPermission (roles, route) {
if (route.meta && route.meta.permission) {
// some循环 当内部return true时跳出整个循环
return roles.some(role => route.meta.permission.includes(role.permission))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes (routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [], // 和左侧绑定的路由数组表
addRouters: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRouters = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = { // roles是用户所带的权限 GenerateRoutes()用于生成当前用户可以访问的路由
GenerateRoutes ({ commit }, data) {
return new Promise(resolve => {
const { roles } = data
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
constantRoutes 静态路由(不需要权限的路由),asyncRoutes 动态路由(需要权限的路由)
hasPermission()方法是用来判断后台给的和你自己本地的是否能匹配的 ,我的是和后台商量好,用meta.permission来做判断,在src\router\index.js 存放所有路由的地方
permission: ['member:add']是和后台商量一起用的,后台返回member:add这个字段,我就用此字段和我本地路由做匹配,匹配出来就把他添加到动态路由accessedRoutes。
GenerateRoutes方法也就是在上面调用的方法,到此已经拿到了,找到侧边栏渲染出来即可,在src\layout\components\Sidebar\index.vue
从vuex用映射的方式获取到权限侧边栏列表数组,this.$router.options.routes此来源看src/permission.js文件,到此就结束了,还有按钮的权限,下一篇见