位置: 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英伟达显卡天梯图完整版)

  • 先悦己,还是先悦众(悦己先悦心)

    先悦己,还是先悦众(悦己先悦心)

  • we game是什么软件(wegameapps是什么)

    we game是什么软件(wegameapps是什么)

  • 优酷账号分享不合法IP上限(优酷怎么没分享了)

    优酷账号分享不合法IP上限(优酷怎么没分享了)

  • 网页banner是啥(网页banner是什么)

    网页banner是啥(网页banner是什么)

  • 热水器一级二级三级有什么区别(热水器一级二级什么意思)

    热水器一级二级三级有什么区别(热水器一级二级什么意思)

  • 苹果11打电话无网络(苹果11打电话无声,重启可以)

    苹果11打电话无网络(苹果11打电话无声,重启可以)

  • 苹果11设置面容一直说低一点(苹果11设置面容ID不可用)

    苹果11设置面容一直说低一点(苹果11设置面容ID不可用)

  • 拼多多子账号有什么用(拼多多子账号有出生日期吗怎么查)

    拼多多子账号有什么用(拼多多子账号有出生日期吗怎么查)

  • 魅族17pro多少倍变焦(魅族17pro多少倍数码变焦)

    魅族17pro多少倍变焦(魅族17pro多少倍数码变焦)

  • 怎么删除本地音乐(怎么删除本地音乐天气语音)

    怎么删除本地音乐(怎么删除本地音乐天气语音)

  • 华为手机有视频美颜功能吗(华为手机有视频声音找不到来源)

    华为手机有视频美颜功能吗(华为手机有视频声音找不到来源)

  • 耳机没有麦克风怎么办(电脑连接蓝牙耳机没有麦克风)

    耳机没有麦克风怎么办(电脑连接蓝牙耳机没有麦克风)

  • iphonexsmax单卡和双卡什么区别(iphonexsmax单卡版有什么不一样)

    iphonexsmax单卡和双卡什么区别(iphonexsmax单卡版有什么不一样)

  • ipad6是air2吗(平板6是air2吗)

    ipad6是air2吗(平板6是air2吗)

  • 未找到应用程序是什么意思(未找到应用程序需要的数据库didb-gfs)

    未找到应用程序是什么意思(未找到应用程序需要的数据库didb-gfs)

  • 删除苹果自带软件后果(删除苹果自带软件可以增加内存吗)

    删除苹果自带软件后果(删除苹果自带软件可以增加内存吗)

  • 华为畅享10提示灯在哪(华为畅享10提示灯)

    华为畅享10提示灯在哪(华为畅享10提示灯)

  • 华为手机看视频模糊怎么回事(华为手机看视频自动暂停怎么回事)

    华为手机看视频模糊怎么回事(华为手机看视频自动暂停怎么回事)

  • 红米手机如何截长图(红米手机如何截长屏图片)

    红米手机如何截长图(红米手机如何截长屏图片)

  • 取消呼叫转移的方法(取消呼叫转移的方法vivo)

    取消呼叫转移的方法(取消呼叫转移的方法vivo)

  • 淘宝邮政编码在哪里(淘宝填写邮政编码)

    淘宝邮政编码在哪里(淘宝填写邮政编码)

  • 手机录制功能怎么开启(手机录制功能怎么弄的)

    手机录制功能怎么开启(手机录制功能怎么弄的)

  • 嗨来电锁屏广告怎么关(来电秀锁屏广告怎么去掉)

    嗨来电锁屏广告怎么关(来电秀锁屏广告怎么去掉)

  • vivo手机格式化账户密码是什么(vivo手机格式化了怎么恢复数据)

    vivo手机格式化账户密码是什么(vivo手机格式化了怎么恢复数据)

  • 不同vlan之间如何通信(不同vlan之间能ping吗)

    不同vlan之间如何通信(不同vlan之间能ping吗)

  • uniapp自定义动态tabbar及全局挂载底部自定义组件(uniapp动态设置标题)

    uniapp自定义动态tabbar及全局挂载底部自定义组件(uniapp动态设置标题)

  • Python psd-tools如何转换文件

    Python psd-tools如何转换文件

  • 税务机关地区编号查询
  • 资产组可收回金额包含商誉吗
  • 技术类服务行业用不用开外经证
  • 2019小规模纳税人税率
  • 商业地产运营成本核算
  • 收取外部客户电费如何结转成本
  • 现金折扣税前扣除
  • 营改增后建筑企业财务核算
  • 所得税营业成本包括哪些
  • 办房产证要交什么税,哪里交?
  • 关于小规模纳税人的说法错误的有
  • 企业注销后款项怎么结算
  • 融资融券主要是哪些对象
  • 基本户没开 可以销户吗
  • 转出未交增值税借方余额表示什么
  • qctray.exe - qctray进程 是什么文件 作用是什么
  • 债权人和债务人是什么意思
  • 收到厂家返利怎么做分录
  • 如何删除文档中多余的页
  • PHP:mb_substr_count()的用法_mbstring函数
  • 苹果电脑出现macos什么意思
  • phpfopen
  • php调用soap
  • 高新技术企业税率15%
  • 网站国际化方案
  • sovies
  • 服务,不动产和无形资产怎么填小规模
  • 软件研发支出计入什么科目
  • 如何根据科目余额表编制财务报表
  • php示例代码
  • 知识图谱ui
  • 装修公司管理费是什么
  • 图书免增值税具体包括哪些
  • 交通费比例
  • 织梦森林官网
  • Spring(十五):Spring整合MyBatis的两种方式
  • 汽车维修费可以做差旅费吗
  • mysql_real_query
  • 减值准备需要确认递延所得税资产吗
  • 财务报表没申报怎么操作呢
  • 小规模企业提现到自己个人银行卡需要交多少税
  • 房屋估价需要准备什么
  • 异价调拨什么意思
  • 开经销商公司要多少钱
  • 工地零电零水布置图
  • 仓库员工工资
  • 包装物范围
  • 宾馆里面的牙膏收费吗
  • 个人独资企业取得拆迁补偿款税收规定
  • sql server入门新手教程
  • sql数据库死锁产生的原因及解决方案
  • mysql无法创建函数
  • win7旗舰版系统恢复
  • linux快速清空大日志文件
  • 0x80070718配额不足
  • windows8文件夹怎么加密
  • 高县土火锅历史介绍
  • 在win7系统中如何用快捷键复制文件或文件夹
  • uefi bios ufilify
  • linux文件权限的设置与修改
  • win7系统如何修改锁屏密码
  • win7ie8和ie11
  • win8桌面在c盘的路径
  • Linux下OpenVPN配置静态密钥(static-key)验证教程
  • node.js使用教程
  • shell脚本连接服务器
  • jquery 定位
  • JQuery ZTree使用方法详解
  • ssh自动输入密码登录
  • html标签页效果
  • linux归档文件什么意思
  • 使用vue-cli快速搭建vue项目
  • nodejs model
  • jquery遍历dom
  • echarts图表
  • void方法设置断言
  • 电子税务局怎么删除办税员
  • 江苏省发票查询电话
  • 营改增后的工程造价怎么算
  • 网上预约税务局怎么弄
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设