权限验证
页面级别权限
路由:
默认挂载不需要权限的路由,例如:登录、主页。需要权限的页面通过 (点击查看官方文档) 动态添加更多的路由规则,404拦截页面需要放在路由表的最后,否则 /404 后面的路由会被404拦截,通过(点击查看官方文档)记录路由需要的权限。为了菜单列表可以被翻译,路由表的 name 属性值通过 i18n 的英文对照表来获取,也可以直接写英文名称,如 name: routeNmae.builtInIcon
可以直接写成 name: "builtInIcon"
,凭个人喜好
// src/router/index.jsimport en from '../i18n/lang/en' // 路由名字 name import Vue from 'vue'import Router from 'vue-router'import CommerViews from '@/views/commerViews'import Login from '@/views/login/index'import Layout from '@/views/layout/layout'import HomeMain from '@/views/index/mainIndex'// 不是必须加载的组件使用懒加载const Icon = () => import('@/views/icon/index')const Upload = () => import('@/views/upload/upload')const Markdown = () => import('@/views/markdown/markdownView')const NotFound = () => import('@/page404')Vue.use(Router)let routeNmae = en.routeNmae// 不需要权限的路由let defaultRouter = [ { path: '/', redirect: '/index', hidden: true, children: [] }, { path: '/login', component: Login, name: '', hidden: true, children: [] }, { path: '/index', iconCls: 'fa fa-dashboard', // 菜单图标,直接填写字体图标的 class name: routeNmae.home, component: Layout, alone: true, children: [ { path: '/index', iconCls: 'fa fa-dashboard', name: '主页', component: HomeMain, children: [] } ] }, { path: '/404', component: NotFound, name: '404', hidden: true, children: [] },]// 需要 addRouters 动态加载的路由 let addRouter = [ { path: '/', iconCls: 'fa fa-server', name: routeNmae.multiDirectory, component: Layout, children: [ { path: '/erji1', iconCls: 'fa fa-server', name: routeNmae['menu2-1'], component: Erji, children: [] }, { path: '/erji3', iconCls: 'fa fa-server', name: routeNmae['menu2-3'], component: CommerViews, // 无限极菜单的容器 超过三级菜单父级容器需要使用 CommerViews children: [ { path: '/sanji2', iconCls: 'fa fa-server', name: routeNmae['menu3-2'], component: Sanji2, children: [] }, { path: '/sanji3', iconCls: 'fa fa-server', name: routeNmae['menu3-3'], component: CommerViews, children: [ { path: '/siji', iconCls: 'fa fa-server', name: routeNmae['menu4-1'], component: Siji, children: [] }, { path: '/siji1', iconCls: 'fa fa-server', name: routeNmae['menu4-2'], component: CommerViews, children: [ { path: '/wuji', iconCls: 'fa fa-server', name: routeNmae['menu5-1'], component: Wuji, children: [] } ] } ] } ] } ] }, { path: '/', iconCls: 'el-icon-edit', // 图标样式class name: routeNmae.editor, component: Layout, meta: {role: ['superAdmin', 'admin']}, // 需要权限 'superAdmin', 'admin'。meta属性可以放在父级,验证父级和所有子菜单,也可以放在子级单独验证某一个子菜单 children: [ { path: '/markdown', iconCls: 'fa fa-file-code-o', // 图标样式class name: routeNmae.markdown, component: Markdown, children: [] } ] }, { path: '*', redirect: '/404', hidden: true, children: [] },]export default new Router({ routes: defaultRouter})export {defaultRouter, addRouter}
然后通过 token
获取当前登录用户的个人信息,在router
被挂载到Vue之前和需要权限的路由表做对比,筛选出当前角色的动态路由表,
// main.js// 获取角色信息,根据用户权限动态加载路由router.beforeEach((to, from, next) => { if (store.getters.token) { // 查看 token 是否存在 store.dispatch('setToken', store.getters.token) // 每次操作都重新写入 token,延长有效会话时间 if (to.path === '/login') { next({path: '/'}) } else { if (!store.getters.info.role) { // 查看是否有当前用户角色,如果没有则获取角色信息 !async function getAddRouters () { await store.dispatch('getInfo', store.getters.token) // 通过token获取角色信息 await store.dispatch('newRoutes', store.getters.info.role) // 通过权限筛选新路由表 await router.addRoutes(store.getters.addRouters) // 动态加载新路由表 next({path: '/index'}) }() } else { let is404 = to.matched.some(record => { // 404页面拦截 if(record.meta.role){ // 没有权限的页面,跳转的404页面 return record.meta.role.indexOf(store.getters.info.role) === -1 } }) if(is404){ next({path: '/404'}) return false } next() } } } else { if (to.path === '/login') { next() } next({path: '/login'}) }})
actions: getInfo
// src/vuex/modules/role.jsstate: { info: '' // 每次刷新都要通过token请求个人信息来筛选动态路由 }, mutations: { getInfo (state, token) { // 省略 axios 请求代码 通过 token 向后台请求用户权限等信息,这里用假数据赋值 state.info = { role: 'superAdmin', permissions: '超级管理员' } // 将 info 存储在 sessionStorage里, 按钮指令权限将会用到 sessionStorage.setItem('info', JSON.stringify(store.getters.info)) }, setRole (state, options) { // 切换角色,测试权限管理 state.info = { role: options.role, permissions: options.permissions } sessionStorage.setItem('info', JSON.stringify(store.getters.info)); // 权限切换后要根据新权限重新获取新路由,再走一遍流程 store.dispatch('newRoutes', options.role) router.addRoutes(store.getters.addRouters) } }, actions: { getInfo ({commit}, token) { commit('getInfo', token) }, setRole ({commit}, options){// 切换角色,测试权限管理,不需要可以删除 commit('setRole', options) } }
actions: newRoutes
// src/vuex/modules/routerData.jsimport {defaultRouter, addRouter} from '@/router/index'const routerData = {state: { routers: [], addRouters: [] }, mutations: { setRouters: (state, routers) => { state.addRouters = routers // 保存动态路由用来addRouter state.routers = defaultRouter.concat(routers) // 所有有权限的路由表,用来生成菜单列表 } }, actions: { newRoutes ({commit}, role) { // 通过递归路由表,删除掉没有权限的路由 function eachSelect (routers, userRole) { for (let j = 0; j < routers.length; j++) { if (routers[j].meta && routers[j].meta.role.length && routers[j].meta.role.indexOf(userRole) === -1) { // 如果没有权限就删除该路由,如果是父级路由没权限,所有子菜单就更没权限了,所以一并删除 routers.splice(j, 1) j = j !== 0 ? j - 1 : j // 删除掉没有权限的路由后,下标应该停止 +1,保持不变,如果下标是 0的话删除之后依然等于0 } if (routers[j].children && routers[j].children.length) { // 如果包含子元素就递归执行 eachSelect(routers[j].children, userRole) } } } // 拷贝这个数组是因为做权限测试的时候可以从低级切回到高级角色,仅限演示,正式开发时省略这步直接使用 addRouter // 仅限演示 let newArr = [...addRouter] eachSelect(newArr, role) commit('setRouters', newArr) // 正式开发 // eachSelect(addRouter, role) // commit('setRouters', addRouter) } }}export default routerData
按钮级别权限验证
通过自定义指令获取当前按钮所需的有哪些权限,然后和当前用户的权限对比,如果没有权限则删除按钮
// btnPermission.jsimport Vue from 'vue'Vue.directive('roleBtn',{ bind:function (el,binding) { let roleArr = binding.value; // 获取按钮所需权限 let userRole = JSON.parse(sessionStorage.getItem('info')).role // 获取当前用户权限 if (roleArr && roleArr.indexOf(userRole) !== -1) { return false } else { el.parentNode.removeChild(el); } }})export default Vue
使用自定义指令权限
查看 添加 删除 修改