🧑🎓 个人主页:SilenceLamb
📖 本章内容:【动态路由+权限校验】
🌳Gitee仓库地址:👉🏽全局前置守卫
一、获取权限修饰符
- 🍑 前提是需要把权限是修饰符存储中store中
- 🍑 src/store/modules/user.js
permissions: state => state.user.permissions,
二、动态路由
🍑 路由信息格式
"name": "System",
"path": "/system",
"hidden": false,
"redirect": "noRedirect",
"component": "Layout",
"alwaysShow": true,
"meta": {
"title": "系统管理",
"icon": "system",
"noCache": false,
"link": null,
"metaVos": null
},
"children": [{
"name": "User",
"path": "user",
"hidden": false,
"component": "system/user/index",
"meta": {
"title": "用户管理",
"icon": "user",
"noCache": false,
"link": null,
"metaVos": null
}
}, ]
🍑 路由配置
- 🍓 src/router: 公共路由配置
// 公共路由
export const constantRoutes = [
{
path: '/',
component: Layout,
redirect: 'index',
children: [{
path: 'index',
name:'Index',
component: () => import('@/views/index/admin'),
meta: {title: '首页', icon: 'dashboard'}
}]
},
{
path: '/login',
component: () => import('@/views/login/login'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/error/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
},
]
- 🍓 src/router: 动态路由,基于用户权限动态去加载
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: {title: '分配角色', activeMenu: '/system/user'}
}
]
},
]
🍑生成路由
- 🍓 src/store/modules/permission.js: 动态生成路由
const state = {
routes: [],
addRoutes: [],
sidebarRouters: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
},
}
const actions = {
// 生成路由
GenerateRoutes({commit}) {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
const data = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(data)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
rewriteRoutes.push({path: '*', redirect: '/404', hidden: true})
router.addRoutes(asyncRoutes);
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
resolve(rewriteRoutes)
})
})
}
}
- 🍓 遍历后台传来的路由字符串,转换为组件对象
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => {
if (type && route.children) {
route.children = filterChildren(route.children)
}
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else if (route.component === 'ParentView') {
route.component = ParentView
} else if (route.component === 'InnerLink') {
route.component = InnerLink
} else {
route.component = loadView(route.component)
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type)
} else {
delete route['children']
delete route['redirect']
}
return true
})
}
- 🍓 动态路由遍历,验证是否具备权限
//动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
const res = []
routes.forEach(route => {
if (route.permissions) {
if (permission.hasPermissionOr(route.permissions)) {
res.push(route)
}
} else if (route.roles) {
if (permission.hasRoleOr(route.roles)) {
res.push(route)
}
}
})
return res
}
- 🍓 子路由过滤
function filterChildren(childrenMap, lastRouter = false) {
let children = []
childrenMap.forEach((el) => {
if (el.children && el.children.length) {
if (el.component === 'ParentView' && !lastRouter) {
el.children.forEach(c => {
c.path = el.path + '/' + c.path
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c))
return
}
children.push(c)
})
return
}
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path
}
children = children.concat(el)
})
return children
}
export const loadView = (view) => {
if (process.env.NODE_ENV === 'development') {
return (resolve) => require([`@/views/${view}`], resolve)
} else {
// 使用 import 实现生产环境的路由懒加载
return () => import(`@/views/${view}`)
}
}
- 🍓 src/store/getters.js 存储路由信息
permission_routes: state => state.permission.routes,
🍑 全局前置守卫
- 🍓 src/permission.js: 全局前置守卫调用生成路由方法
store.dispatch('permission/GenerateRoutes').then(accessRoutes => {
// 根据roles权限生成可访问的路由表
accessRoutes.forEach(item => {
router.addRoute(item) // 动态添加可访问路由表
})
next({...to, replace: true}) // hack方法 确保addRoutes已完成
})
三、Layout引入动态路由
🍎 Layout引入动态路由
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"/>
export default {
...mapGetters(["sidebarRouters", "sidebar"]),
}
🍎 保持一个子菜单的展开
你可以在Sidebar/index.vue中设置unique-opened来控制侧边栏,是否只保持一个子菜单的展开。
🍎 点击侧边栏 刷新当前路由
activeMenu() {
const {meta, path} = this.$route
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
四、权限验证方法
import store from '@/store'
/**
* 字符权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkPermission(value) {
if (value && value instanceof Array && value.length > 0) {
const permissions = store.getters && store.getters.permissions
const permissionData = value
const all_permission = "*:*:*";
const hasPermission = permissions.some(permission => {
return all_permission === permission || permissionData.includes(permission)
})
if (!hasPermission) {
return false
}
return true
} else {
console.error(`need roles! Like checkPermission="['system:user:add','system:user:edit']"`)
return false
}
}
export function authPermission(permission) {
const all_permission = "*:*:*";
const permissions = store.getters && store.getters.permissions
if (permission && permission.length > 0) {
return permissions.some(v => {
return all_permission === v || v === permission
})
} else {
return false
}
}
function authRole(role) {
const super_admin = "admin";
const roles = store.getters && store.getters.roles
if (role && role.length > 0) {
return roles.some(v => {
return super_admin === v || v === role
})
} else {
return false
}
}
/**
* 验证用户是否含有指定权限,只需包含其中一个
* @param permissions
* @returns {*}
*/
export function hasPermissionOr(permissions) {
return permissions.some(item => {
return authPermission(item)
})
}
/**
* 验证用户是否含有指定角色,只需包含其中一个
* @param roles
* @returns {*}
*/
export function hasRoleOr(roles) {
return roles.some(item => {
return authRole(item)
})
}
/**
* 角色权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkRole(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const super_admin = "admin";
const hasRole = roles.some(role => {
return super_admin === role || permissionRoles.includes(role)
})
if (!hasRole) {
return false
}
return true
} else {
console.error(`need roles! Like checkRole="['admin','editor']"`)
return false
}
}
import store from '@/store'
function authPermission(permission) {
const all_permission = "*:*:*";
const permissions = store.getters && store.getters.permissions
if (permission && permission.length > 0) {
return permissions.some(v => {
return all_permission === v || v === permission
})
} else {
return false
}
}
function authRole(role) {
const super_admin = "admin";
const roles = store.getters && store.getters.roles
if (role && role.length > 0) {
return roles.some(v => {
return super_admin === v || v === role
})
} else {
return false
}
}
export default {
// 验证用户是否具备某权限
hasPermission(permission) {
return authPermission(permission);
},
// 验证用户是否含有指定权限,只需包含其中一个
hasPermissionOr(permissions) {
return permissions.some(item => {
return authPermission(item)
})
},
// 验证用户是否含有指定权限,必须全部拥有
hasPermissionAnd(permissions) {
return permissions.every(item => {
return authPermission(item)
})
},
// 验证用户是否具备某角色
hasRole(role) {
return authRole(role);
},
// 验证用户是否含有指定角色,只需包含其中一个
hasRoleOr(roles) {
return roles.some(item => {
return authRole(item)
})
},
// 验证用户是否含有指定角色,必须全部拥有
hasRoleAnd(roles) {
return roles.every(item => {
return authRole(item)
})
}
}
import permission from './permission'
export default {
install(Vue) {
Vue.prototype.$auth = permission
}
}
- 🍒 安装插件: src/main.js
import plugins from './utils/plugins' // plugins
Vue.use(plugins)
五、按钮鉴权标签
🍒 v-has-permission 操作权限处理
- 🍓 定义按钮鉴权标签,这里采用插件的形式
- 👉🏽 src/directive/permission/hasPermission.js
/**
* v-hasPermission 操作权限处理
*/
import store from '@/store'
export default {
inserted(el, binding) {
const { value } = binding
const all_permission = "*:*:*";
const permissions = store.getters && store.getters.permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}
🍒 v-has-role 操作权限处理
- 🍓定义按钮鉴权标签,这里采用插件的形式
- 👉🏽src/directive/permission/hasRole.js
/**
* v-hasRole 操作权限处理
*/
import store from '@/store'
export default {
inserted(el, binding) {
const {value} = binding
const super_admin = "admin";
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0) {
const roleFlag = value
const hasRole = roles.some(role => {
return super_admin === role || roleFlag.includes(role)
})
if (!hasRole) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置角色权限标签值"`)
}
}
}
- 🍓 src/directive/index.js
import hasRole from './permission/hasRole'
import hasPermission from './permission/hasPermission'
import Vue from "vue";
const install = function(Vue) {
Vue.directive('hasRole', hasRole)
Vue.directive('hasPermission', hasPermission)
}
if (window.Vue) {
window['hasRole'] = hasRole
window['hasPermission'] = hasPermission
Vue.use(install); // eslint-disable-line
}
export default install
- 🍓安装插件 src/main.js
import directive from './directive' // directive
Vue.use(directive)
v-has-permission: 按钮权限使用方法
- 🍒 v-hasPermission="['system:user:add']"
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
v-has-permission="['system:user:add']"
>新增</el-button>
📢🌥️如果文章对你有帮助【关注👍点赞❤️收藏⭐】