位置: IT常识 - 正文

Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码(vue角色管理)

编辑:rootadmin
Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码 前言

推荐整理分享Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码(vue角色管理),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue权限怎么做,vue用户权限解决方案,vue角色权限系统,vue角色权限管理系统,vue角色权限页面,vue角色权限管理系统,vue角色权限系统,vue角色权限管理系统,内容如对您有帮助,希望把文章链接给更多的朋友!

vue-admin-template是一个最基础的后台管理模板,只包含了一个后台需要最基础的东西,如果clone的是它的master分支,是没有权限管理的,只有完整版vue-element-admin有这个功能,但是为了小小的一个权限管理而用比较复杂的有点得不偿失。

我在网上找了一堆教程和资料,发现要么说的很乱,要么说的不全,最后连个完整代码都不让我白嫖(bushi)。自己复制粘贴过去都实现不出来,仔细查看发现人家写的教程漏了一写步骤/代码,而且还有bug(服了这些老六)。

在自己摸索了和看了花裤衩大佬的文章后,解决了一些bug自己实现出来了,代码中也有详细注释。完整代码放文末给大家了,大家记得给我star再走(不然小拳拳锤你胸口)。

权限管理?动态路由?

现在开发后台管理系统项目经常有权限管理的需求,权限管理其实就是根据不同的角色权限显示不同的路由,而其中的关键就是动态路由router.addRoutes 实现权限验证的基本思路就是:

用户登录,通过token获取用户对应的 role动态根据用户的 role 算出其对应有权限的路由通过 router.addRoutes 动态挂载这些路由

以上步骤实现的核心是router和vuex,下面就详细介绍如何实现(按代码执行逻辑倒推)

具体实现创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件

本段转载自vue-element-admin的作者:花裤衩

1.router.js路由表

首先我们实现router.js路由表, 一共有两个路由表,

一个是constantRoutes,这个用来放没有权限要求的页面,每个角色都可以访问,比如首页,登录页;一个是asyncRoutes 动态需要根据权限加载的路由表 。meta里面的roles就存放了页面需要的权限,这些页面只有roles数组里面的角色才能看到。Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码(vue角色管理)

注意:404一定要放最后面,不然都会重定向到404 src/router/index.js代码如下:

import Vue from 'vue'import Router from 'vue-router'Vue.use(Router)/* Layout */import Layout from '@/layout'/** * constantRoutes * 没有权限要求的基本页面 *所有角色都可以访问 如首页和登录页和一些不用权限的公用页面 */export const constantRoutes = [{ path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', children: [{ path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'Dashboard', icon: 'dashboard', } }] },]//异步挂载的路由//动态需要根据权限加载的路由表export const asyncRoutes = [{ path: '/example', component: Layout, redirect: '/example/table', name: 'Example', alwaysShow: true, meta: { title: 'Example', icon: 'el-icon-s-help', }, children: [{ path: 'table', name: 'Table', component: () => import('@/views/table/index'), meta: { title: 'Table', icon: 'table', roles: ['editor'] } }, { path: 'tree', name: 'Tree', component: () => import('@/views/tree/index'), meta: { title: 'Tree', icon: 'tree', roles: ['admin', 'editor'] } } ] }, { path: '/form', component: Layout, children: [{ path: 'index', name: 'Form', component: () => import('@/views/form/index'), meta: { title: 'Form', icon: 'form', roles: ['editor'] } }] }, { path: '/nested', component: Layout, redirect: '/nested/menu1', alwaysShow: true, name: 'Nested', meta: { title: 'Nested', icon: 'nested', }, children: [{ path: 'menu1', component: () => import('@/views/nested/menu1/index'), // Parent router-view name: 'Menu1', meta: { title: 'Menu1', roles: ['admin'] }, children: [{ path: 'menu1-1', component: () => import('@/views/nested/menu1/menu1-1'), name: 'Menu1-1', meta: { title: 'Menu1-1' } }, { path: 'menu1-2', component: () => import('@/views/nested/menu1/menu1-2'), name: 'Menu1-2', meta: { title: 'Menu1-2' }, children: [{ path: 'menu1-2-1', component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'), name: 'Menu1-2-1', meta: { title: 'Menu1-2-1' } }, { path: 'menu1-2-2', component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'), name: 'Menu1-2-2', meta: { title: 'Menu1-2-2' } } ] }, { path: 'menu1-3', component: () => import('@/views/nested/menu1/menu1-3'), name: 'Menu1-3', meta: { title: 'Menu1-3' } } ] }, { path: 'menu2', component: () => import('@/views/nested/menu2/index'), name: 'Menu2', meta: { title: 'menu2', roles: ['editor'] } } ] }, // 如果需要配置重定向404页面的话,需要配置在asyncRoutes的最后 { path: '*', redirect: '/404', hidden: true }]// 实例化vue的时候只挂载constantRouterconst createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), routes: constantRoutes})const router = createRouter()// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router}export default router2. src/permission.js动态添加路由

我们在登录成功后,router会重定向一个新页面,在跳转之前src/permission.js里面路由守卫router.beforeEach会先做一些拦截验证,根据判断会做不同页面跳转和操作,比如没登录就先跳到登录页,根据获取到的用户信息roles筛选路由后动态添加路由。 src/permisson.js具体实现代码与注释:

import router from './router'import store from './store'import { Message} from 'element-ui'// 页面进度条组件import NProgress from 'nprogress' // progress barimport 'nprogress/nprogress.css' // progress bar styleimport { getToken} from '@/utils/auth' // get token from cookieimport getPageTitle from '@/utils/get-page-title'NProgress.configure({ showSpinner: false}) // NProgress 配置const whiteList = ['/login', '/404'] // 不重定向的白名单router.beforeEach(async (to, from, next) => { // start progress bar NProgress.start() // 设置页面标题 document.title = getPageTitle(to.meta.title) // 确定用户是否已登录 const hasToken = getToken() // 判断是否存在token,没有就重新登陆 if (hasToken) { if (to.path === '/login') { // 如果已登录,则重定向到主页 next({ path: '/' }) NProgress.done() } else { // 确定用户是否通过getInfo获得了权限角色 const hasRoles = store.getters.roles && store.getters.roles.length > 0 //这里指的是src/store/getters.js的roles // console.log(hasRoles) //判断是否已经有roles了 if (hasRoles) { next(); //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面 } else { try { // get user info // 注意: roles 角色必须是对象数组! 例如: ['admin'] 或 ,['developer','editor'] // 1. 获取roles const { roles } = await store.dispatch('user/getInfo') //第一步 // 2. 根据角色生成可访问路由图 // 获取通过权限验证的路由 const accessRoutes = await store.dispatch('permission/generateRoutes', roles) //第二步 // 3. 更新加载路由 router.options.routes = store.getters.permission_routes //第三步 // 动态添加可访问路由 router.addRoutes(accessRoutes) // console.log(store) // console.log(accessRoutes); // hack方法 确保addRoutes已完成,以确保地址是完整的 // 设置replace: true,这样导航就不会留下历史记录 next({ ...to, replace: true }) } catch (error) { // 删除token并转到登录页面重新登录 await store.dispatch('user/resetToken') Message.error('出现错误~请重新登录') next(`/login?redirect=${to.path}`) NProgress.done() } } } } else { /* 没有token */ if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 next() } else { next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 NProgress.done() } }})3. store/modules/user.js

而获取+存储roles通过store/modules/user.js实现,筛选+存储路由又是通过通过store/modules/permission.js实现的 store/modules/user.js主要是获取和存储roles store/modules/user.js完整代码:

import { login, logout, getInfo} from '@/api/user'import { getToken, setToken, removeToken} from '@/utils/auth'import { resetRouter} from '@/router'const getDefaultState = () => { return { token: getToken(), name: '', avatar: '', roles: [], }}const state = getDefaultState()const mutations = { RESET_STATE: (state) => { Object.assign(state, getDefaultState()) }, SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { state.name = name }, SET_ROLES: (state, roles) => { state.roles = roles }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }}const actions = { // user login login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => { const { data } = response commit('SET_TOKEN', data.token) setToken(data.token) resolve() }).catch(error => { reject(error) }) }) }, // get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { // state.token之前没有传 出现了重复登陆问题 getInfo(state.token).then(response => { const { data } = response if (!data) { return reject('验证失败,请重新登录') } const { name, roles, avatar } = data if (!roles || roles.length <= 0) { reject('getInfo:roles must be a non-null array!') } commit('SET_NAME', name) commit('SET_ROLES', roles) commit('SET_AVATAR', avatar) resolve(data) }).catch(error => { reject(error) }) }) }, // user logout logout({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { removeToken() // must remove token first resetRouter() commit('RESET_STATE') commit('SET_ROLES', []) resolve() }).catch(error => { reject(error) }) }) }, // remove token resetToken({ commit }) { return new Promise(resolve => { removeToken() // must remove token first commit('RESET_STATE') resolve() }) }}export default { // 加上这个会有报错,不加的话user/login这种方式用不了 namespaced: true, state, mutations, actions}4. store/modules/permission.js筛选路由

store/modules/permission.js用于匹配权限,筛选角色对应的路由并存储起来

import { asyncRoutes, constantRoutes} from '@/router'/** * 使用 meta.role 以确定当前用户是否具有权限 * @param roles * @param route */// 匹配权限function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { return true }}/** * 通过递归过滤异步路由表 * @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: [], addRoutes: []}const mutations = { SET_ROUTES: (state, routes) => { state.addRoutes = routes state.routes = constantRoutes.concat(routes) // 将过滤后的路由和constantRoutes存起来 }}// 筛选const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes // 管理员admin显示全部路由, // 我这里admin是想让它不显示全部的 想要admin能看见全部的话把注释去掉 // if (roles.includes('admin')) { // accessedRoutes = asyncRoutes || [] // } else { //过滤路由 accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) // accessedRoutes这个就是当前角色可见的动态路由 // } commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) }}export default { namespaced: true, state, mutations, actions}

这里的代码说白了就是干了一件事,通过用户的权限和之前在router.js里面asyncRoutes的每一个页面所需要的权限做匹配,最后返回一个该用户能够访问路由有哪些。

5. store/getter.js和store/index.js

当然了,新加的模块要记得引入进去 store/getter.js添加以下代码:

store/index.js

6. sidebar使用筛选后的路由

src\layout\components\Sidebar\index.vue 遍历之前算出来的permission_routers,通过vuex拿到之后动态v-for渲染

<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />效果

如何看效果?如果是用的vue-admin-template的mock做登录和获取用户数据,登录用户名为admin则role为admin,用户名为editor则role为editor。 角色为admin看到的菜单栏

角色为editor看到的菜单栏

完整源码

记得给我一个star https://gitee.com/yyy1203/vue-admin-template-permission.git

有兴趣可以看看花裤衩大佬文章:https://juejin.cn/post/6844903478880370701#heading-5

本文链接地址:https://www.jiuchutong.com/zhishi/298484.html 转载请保留说明!

上一篇:VUE项目部署(vue项目部署后白屏)

下一篇:2023英伟达显卡排名天梯图(已更新)(2023英伟达显卡天梯图完整版)

  • 一加手机怎么查激活时间(一加手机怎么查imei号码)

    一加手机怎么查激活时间(一加手机怎么查imei号码)

  • 电话单显示12599(来电显示12599是啥意思)

    电话单显示12599(来电显示12599是啥意思)

  • 逻辑id是什么(逻辑id是什么样的)

    逻辑id是什么(逻辑id是什么样的)

  • 小米手机怎么录制视频(小米手机怎么录视频)

    小米手机怎么录制视频(小米手机怎么录视频)

  • 华为手机闹铃声音在哪设置(华为手机闹铃声音小)

    华为手机闹铃声音在哪设置(华为手机闹铃声音小)

  • 截图用什么快捷键(截图哪个快捷键)

    截图用什么快捷键(截图哪个快捷键)

  • 华为咋不显示运行内存(华为手机怎么不显示运动数据?)

    华为咋不显示运行内存(华为手机怎么不显示运动数据?)

  • 微信双方都拉黑对方再怎么加上(微信双方都拉黑头像变灰白)

    微信双方都拉黑对方再怎么加上(微信双方都拉黑头像变灰白)

  • 电脑太久没用了,启动不起来怎么办(电脑太久没用了充不上电怎么办)

    电脑太久没用了,启动不起来怎么办(电脑太久没用了充不上电怎么办)

  • 为什么cad打印的时候有些不显示(为什么cad打印的线是波浪线)

    为什么cad打印的时候有些不显示(为什么cad打印的线是波浪线)

  • 小米手机玩游戏黑屏怎么回事(小米手机玩游戏时屏幕变暗)

    小米手机玩游戏黑屏怎么回事(小米手机玩游戏时屏幕变暗)

  • team viewer 远程控制安全吗(teamviewer远程控制电脑能听到声音吗)

    team viewer 远程控制安全吗(teamviewer远程控制电脑能听到声音吗)

  • 所谓的裸机是指(所谓裸机 是指)

    所谓的裸机是指(所谓裸机 是指)

  • word16k纸张怎么设置(word2016纸张)

    word16k纸张怎么设置(word2016纸张)

  • 电脑如何用手写输入(电脑如何用手写版)

    电脑如何用手写输入(电脑如何用手写版)

  • 微信数字证书是啥(微信数字证书是什么保证自己安全的)

    微信数字证书是啥(微信数字证书是什么保证自己安全的)

  • 苹果手机丢了,别人捡到能不能用(苹果手机丢了怎么锁定不让别人用)

    苹果手机丢了,别人捡到能不能用(苹果手机丢了怎么锁定不让别人用)

  • msi是什么主板(msi主板是什么意思)

    msi是什么主板(msi主板是什么意思)

  • PS怎么制作细格子唯美水印(ps如何画细线)

    PS怎么制作细格子唯美水印(ps如何画细线)

  • 华为畅享9plus手机录屏在哪里(华为畅享9plus手机卡顿怎么解决)

    华为畅享9plus手机录屏在哪里(华为畅享9plus手机卡顿怎么解决)

  • 一加七pro尺寸(一加7pro 尺寸)

    一加七pro尺寸(一加7pro 尺寸)

  • 苹果x接电话没声音(苹果x接电话没网络怎么回事)

    苹果x接电话没声音(苹果x接电话没网络怎么回事)

  • 香港手机号码格式(香港手机号码格式不正确)

    香港手机号码格式(香港手机号码格式不正确)

  • win10闹钟如何休眠仍然响(win10闹钟设置方法)

    win10闹钟如何休眠仍然响(win10闹钟设置方法)

  • 金税盘老是重复更新进不去
  • 印花税权利许可证照免税吗
  • 公司付给中间人居间费用如何纳税
  • 债权投资减值对摊余成本和利息收入的影响
  • 个税补缴纳
  • 飞机票火车票汽车票抵扣进项税
  • 会计核算原则主要包括
  • 应交税费未交增值税借方余额表示什么
  • 收购公司固定资产注意事项
  • 所得税季度预缴纳税申报表中弥补以前年度亏损
  • 跨月冲红的发票怎么做账
  • 融资性售后回租和融资租赁的区别
  • 零售通收银
  • 一次性收取跨年租金房产税怎么交
  • 个人承担的社保费用算在员工工资里吗?
  • 收入冲正
  • 银行端现金缴税凭证
  • 超市库存商品分为哪几类
  • 土地增值税进项税
  • 垃圾清运属于什么大类
  • 未确认融资费用属于企业资产类科目对不对
  • 递延所得税资产和递延所得税负债
  • 怎么在bios中开启硬件虚拟化
  • 在win7系统中如何让电脑恢复出厂设置方法
  • php页面之间传值
  • uefi和legacy的区别对显卡兼容
  • ati2mdxx.exe是什么进程 ati2mdxx进程信息查询
  • 盈利 利润
  • 电脑关机了wifi还能检测到在线
  • 劳务公司社保手续办理
  • 货物无偿赠送分录
  • win10蓝牙无法连接,有解决方法吗
  • 异构图神经网络 电影推荐
  • php编程技术
  • 交通费补贴算工资吗?
  • 收取跨年租金会计处理
  • 销售商品怎么做会计分录呢
  • 出口退税是怎么个流程
  • three.js 教程
  • 今天中秋节
  • 残疾人就业保障金计入什么科目
  • 预算会计组成体系不包括
  • 办公室买花卉怎么做分录
  • 员工报销的差旅费属于什么科目
  • 面试官问:mysql 的自增 id 用完了,怎么办?
  • 可供出售债权投资
  • 规划设计费会计处理
  • 利润表管理费用包括哪些内容
  • mongodb skip数据量大
  • 物业公司水费差额征税如何申报
  • 应付账款的余额表示什么
  • 收到银行手续费已做后期收到发票怎么做
  • 企业取得的跨期业务
  • 一次性支付意思
  • 企业给职工发放的自产产品应作为职工福利费管理
  • 外币财务报表的折算
  • 负债表预付账款怎么填
  • 办理三证合一多久能拿到
  • hyper-v怎么样
  • mac截图清晰度设置方法
  • 打开优酷视频播放
  • linux中使用什么命令可以把两个文件合并
  • SWNETSUP.EXE - SWNETSUP是什么进程 有什么用
  • mac如何修改用户名称
  • 怎么安装多个相同软件
  • win7电脑视频预览图如何显示出来
  • win8双系统怎么切换
  • win7怎么删除wifi已连接过的网络
  • linux检测硬件
  • Linux shell实现HTTP服务示例代码
  • python里的set
  • Quick cocos2dx-Lua(V3.3R1)学习笔记(8) ---- 事件篇之单点触摸事件,让我们用精灵模仿一个按钮吧
  • <Unity UGUI>使用c#反射实现UGUI文本显示的国际化
  • node.js教程详细
  • 结合mint-ui移动端下拉加载实践方法总结
  • 北京东城区社保电话
  • 医保预交金可以取出来吗
  • 沧州市养老保险局官网
  • 为什么每个月总有几天
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设