第五章🚦动态路由+权限校验

309 阅读3分钟

🧑‍🎓 个人主页:SilenceLamb

📖 本章内容:【动态路由+权限校验

🌳Gitee仓库地址:👉🏽全局前置守卫


一、获取权限修饰符

permissions: state => state.user.permissions,

image.png


二、动态路由

🍑 路由信息格式

		"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
			}
		}, ]

🍑 路由配置

// 公共路由
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'}
            }
        ]
    },
]

🍑生成路由

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,

🍑 全局前置守卫

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"]),
}

image.png

🍎 保持一个子菜单的展开

你可以在Sidebar/index.vue中设置unique-opened来控制侧边栏,是否只保持一个子菜单的展开。

image.png

🍎 点击侧边栏 刷新当前路由

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>

扫码_搜索联合传播样式-标准色版.png

📢🌥️如果文章对你有帮助【关注👍点赞❤️收藏⭐】