前端单独实现 vue 动态路由

前端单独实现 vue 动态路由

Vue 动态路由权限是指在 Vue 应用程序中,根据用户的权限动态生成和控制路由的行为。这意味着不是所有的路由都在应用启动时就被硬编码到路由配置中,而是根据用户的权限信息,在运行时动态地决定哪些路由应该被加载和显示。

动态路由的优点:

  • 安全性:

    • 只有经过验证的用户才能访问其权限范围内的页面。
    • 减少了由于硬编码路由导致的安全漏洞。
  • 灵活性:

    • 可以根据用户的权限动态调整应用的结构,无需重新部署整个应用即可调整路由。
    • 支持按需加载(懒加载),提高应用性能。
  • 用户体验:

    • 只展示用户可以访问的菜单项,避免显示无用链接,提高用户体验。
    • 用户界面更加简洁,只显示与其角色相关的功能。
  • 可维护性:

    • 简化了路由配置,因为不需要为每个角色单独编写路由配置,而是集中管理权限。
    • 更容易扩展和修改权限配置,只需更新前端的权限数据即可。
  • 开发效率:

    • 开发者只需要关注业务逻辑,而不需要关心每个角色的具体路由配置。
    • 减少了重复工作,提高了开发效率。

实现步骤

  • 定义静态路由配置:

    • 在项目中定义一个包含所有可能路由的静态配置文件或对象,每个路由可以附加权限信息(如角色、访问级别等)。
  • 用户登录与鉴权:

    • 用户登录时,前端存储用户的权限信息(如角色、权限列表等)。
  • 动态生成路由:

    • 根据用户的权限信息,从前端的静态路由配置中筛选出用户有权访问的路由。
    • 使用递归算法或其他逻辑动态生成路由配置,并添加到 Vue Router 实例中。
  • 动态渲染菜单:

    • 根据动态生成的路由表来渲染左侧菜单或顶部导航栏,确保只显示用户有权访问的菜单项。

代码示例

配置路由器

router/index.js

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/Layout/index.vue'Vue.use(VueRouter)// export const roleMap = {
//     '-1':'运维管理员',
//     '1':'普通用户',
//     '2':'项目经理',
//     '3':'部门管理员',
//     '4':'综合部管理员',
//     '5':'部门领导'
// }// 公共路由
export const routes = [{path: '/',name: 'redirect',component: Layout,hidden: true, // 隐藏菜单redirect: "/homePage", // 用户在地址栏输入 '/' 时会自动重定向到 /homePage 页面},{path: '/homePage',component: Layout,redirect: "/homePage/index",meta: {title: "首页",},children: [{path: 'index',name: 'homePageIndex',meta: {title: "首页",},component: () => import('@/views/homePage/index.vue')}]},{path: '/login',component: () => import('@/views/login.vue'),hidden: true},{path: '/404',component: () => import('@/views/error/404.vue'),hidden: true},{path: '/401',component: () => import('@/views/error/401.vue'),hidden: true},
]// 动态权限路由
export const dynamicRoutes = [{path: '/admin',meta: {title: "系统管理",},component: Layout,permission: ['-1', '2', '3', '4', '5'], // all 所有角色都可以访问  1 普通用户  2 项目经理  3 部门管理员  4 综合部管理员  5  部门领导  -1 项目运维管理员children: [{path: 'user',name: 'userIndex',meta: {title: "用户管理",},permission: ['-1', '2', '3', '4', '5'],component: () => import('@/views/admin/user/index.vue')},{path: 'role',name: 'roleIndex',meta: {title: "角色管理",},permission: ['-1', '2', '3', '4', '5'],component: () => import('@/views/admin/role/index.vue'),children: [{path: 'add',name: 'addRole',meta: {title: "添加角色",},permission: ['-1',, '3', '4', '5'],component: () => import('@/views/admin/user/index.vue')},{path: 'update',name: 'updateRole',meta: {title: "编辑角色",},permission: ['-1', '2', '3', '4', '5'],component: () => import('@/views/admin/role/index.vue')}]}]},{path: '/tableEcho',meta: {title: "表格管理",},component: Layout,permission: ['-1', '1', '2'],children: [{path: 'test',name: 'tableEchoIndex',meta: {title: "表格测试",},permission: ['-1', '1', '2'],component: () => import('@/views/tableEcho/index.vue'),children: [{path: 'add',name: 'addTable',hidden: true,meta: {title: "新增测试",},permission: ['-1', '2'],component: () => import('@/views/tableEcho/add.vue')}]},],},
]const router = new VueRouter({base: process.env.BASE_URL,routes
})export default router

上述代码定义了一个公共路由 routes 和一个动态权限控制的路由 dynamicRoutes , permission 数组定义了哪些角色拥有该路由权限, 将用户分为6个角色级别, 每个角色对应不同的角色级别,分别为

  • ‘-1’:‘运维管理员’,
  • ‘1’:‘普通用户’,
  • ‘2’:‘项目经理’,
  • ‘3’:‘部门管理员’,
  • ‘4’:‘综合部管理员’,
  • ‘5’:‘部门领导’,

封装路由守卫

permission.js

// permission.js
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import { getStore } from '@/utils/store';const whiteList = ['/login', '/404', '/401'];router.beforeEach((to, from, next) => {let token = getStore('token');if (token) {/* has token*/if (to.path === '/login') {next({ path: '/' });} else {if (store.getters.roles.length === 0) {// 判断当前用户是否已拉取完user_info信息store.dispatch('GetInfo').then((res) => {console.log('--------------', res);router.addRoutes(res) // 动态添加可访问路由表next({ ...to, replace: true }) // hack方法 确保addRoutes已完成}).catch(err => {store.dispatch('LogOut').then(() => {Message.error(err)next(`/`)})})} else {next()}}} else {// 没有tokenif (whiteList.indexOf(to.path) !== -1) {// 在免登录白名单,直接进入next()} else {next(`/login`) // 否则全部重定向到登录页}}
})

上述代码表示在路由的 beforeEach 函数里面调用 vuex 里面 actions 里的方法发送接口请求获取用户信息与用户角色权限, 最后通过 router.addRoutes(res) 渲染路由
permission.js 文件需引入到 main.js里面

如果项目 vue-router 版本超过 3.3.0, 需要遍历路由数组再使用 router.addRoute() 方法逐个添加路由

res.forEach( route => {router.addRoute(route);
})

在 vuex 里获取用户所拥有的权限, 过滤该权限不拥有的路由

store/index.js

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { routes, dynamicRoutes } from "@/router";
import { login, getInfo, logout } from "@/api/user";
import { setStore, clearStore } from '@/utils/store';Vue.use(Vuex)export default new Vuex.Store({state: {routes,token: "",roleType: "",roles: [],permissions: [],sidebarRouters: [],},getters: {token: state => state.token,roles: state => state.roles,permissions: state => state.permissions,sidebarRouters: state => state.sidebarRouters,},mutations: {SET_TOKEN: (state, token) => {state.token = token;},SET_USERINFO: (state, user) => {state.userInfo = user;},SET_ROLETYPE: (state, roleType) => {state.roleType = roleType;},SET_ROLES: (state, roles) => {state.roles = roles;},SET_PERMISSIONS: (state, permissions) => {state.permissions = permissions;},SET_ROUTE: (state, sidebarRouters) => {state.sidebarRouters = sidebarRouters;},},actions: {Login({ commit }, userInfo) {return new Promise((resolve, reject) => {login(userInfo).then(res => {setToken(res.data.token);setStore('token', res.data.token);commit('SET_TOKEN', res.data.token);resolve();}).catch(error => {reject(error);})})},// 获取用户信息GetInfo({ commit }) {return new Promise((resolve, reject) => {getInfo().then(res => {console.log('res::: ', res);if (res.data.code === 0 || 200) {const user = res.data.sysUser;const roleType = res.data.roleType;commit('SET_USERINFO', user);// roleType 用户所用的权限级别 1 普通用户  2 项目经理  3 部门管理员  4 综合部管理员  5  部门领导  -1 项目运维管理员setStore('ROLE_TYPE', roleType);if (res.data.roles) { // 验证返回的roles是否为真commit('SET_ROLES', res.data.roles);commit('SET_PERMISSIONS', res.data.permissions);} else {commit('SET_ROLES', ['ROLE_DEFAULT']);}// 过滤路由let newRouters = filterRouter(roleType, dynamicRoutes);// 连接公共路由const sidebarRouters = routes.concat([...newRouters])commit('SET_ROUTE', sidebarRouters);resolve(sidebarRouters);} else {reject(error);}}).catch(error => {reject(error);})})},// 退出系统LogOut({ commit, state }) {return new Promise((resolve, reject) => {logout(state.token).then(() => {commit('SET_TOKEN', '')commit('SET_ROLES', [])commit('SET_PERMISSIONS', [])clearStore('token');clearStore('userInfo')resolve()}).catch(error => {reject(error)})})},},modules: {}
})function filterRouter(roleType, routes) {return routes.filter(item => {  // filter 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,测试未通过的元素会自动剔除// 如果一级路由的 permission 含有当前角色的 roleTypeif (item.permission.includes(roleType)) {// 如果该一级路由含有子路由时,递归调用该函数判断子路由是否有权限if (Array.isArray(item.children) && item.children.length > 0) {// 递归调用该函数,最后接受校验通过后的子路由let newChildren = filterRouter(roleType, item.children);// 如果子路由有值,则赋值给当前路由的 children,剔除校验不通过的子路由if (newChildren.length > 0) {item.children = newChildren;} else if (newChildren.length === 0) { // 如果子路由为空,则删除该路由的 children 属性delete item.children;}}// 最后返回 true, 表示该路由通过权限校验return true;}})
}

上述代码通过 getInfo 接口获取用户权限, 通过函数 filterRouter 过滤掉该角色不拥有的路由, 通过 concat 方法合并 routes 公共路由, 最后通过 resolve 返回

文件布局如下

在这里插入图片描述

下图为页面渲染的菜单(项目经理角色)

在这里插入图片描述

左侧菜单实现参考链接: Elemnt-UI + 递归组件实现后台管理系统左侧菜单

前端结合后端接口请求实现动态路由参考连接: 前端 + 接口请求实现 vue 动态路由

总结

在用户登录成功后从服务器获取用户的权限信息,在 vuex 的异步处理函数中过滤掉角色权限不存在的路由,使用 concat() 合并公共路由,最后使用 router.addRoutes(res) 动态添加可访问的路由。这样可以确保应用根据用户的权限动态加载相应的路由,增强安全性与灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/425838.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VirtualBox Install MacOS

环境搭建 git clone https://github.com/myspaghetti/macos-virtualbox 脚本配置 修改macos-guest-virtualbox.sh部分内容为 vm_name"macOS" # name of the VirtualBox virtual machine macOS_release_name"Catalina" # install &quo…

EmguCV学习笔记 C# 11.6 图像分割

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

《微信小程序实战(2) · 组件封装》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

天津大学推出“AI学长”

B站:啥都会一点的研究生公众号:啥都会一点的研究生 AI圈又发生了啥新鲜事? 天津大学推出“AI 学长”海棠棠,全天候解答新生疑问 天津大学未来技术学院研发了名为“海棠棠”的新生智能体,它能够24小时不间断地为新生…

Oracle 19c异常恢复—ORA-01209/ORA-65088---惜分飞

由于raid卡bug故障,导致文件系统异常,从而使得数据库无法正常启动,客户找到我之前已经让多人分析,均未恢复成功,查看alert日志,发现他们恢复的时候尝试resetlogs库,然后报ORA-600 kcbzib_kcrsds_1错误 2024-09-15T17:07:32.55321508:00 alter database open resetlogs 2024-09-…

【iOS】push和present的区别

【iOS】push和present的区别 文章目录 【iOS】push和present的区别前言pushpop presentdismiss简单小demo来展示dismiss和presentdismiss多级 push和present的区别区别相同点 前言 在iOS开发中,我们经常性的会用到界面的一个切换的问题,这里我们需要理清…

C++11新增特性:lambda表达式、function包装器、bind绑定

一、lambda表达式 1)、为啥需要引入lambda? 在c98中,我们使用sort对一段自定义类型进行排序的时候,每次都需要传一个仿函数,即手写一个完整的类。甚至有时需要同时实现排升序和降序,就需要各自手写一个类&…

信息学奥赛初赛天天练-91-CSP-S2023基础题3-编译命令、树的重心、拓扑排序、进制转换、R进制转十进制、十进制转R进制

PDF文档公众号回复关键字:20240917 2023 CSP-S 选择题 1单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项) 11 以下哪个命令,能将一个名为 main.cpp 的 C 源文件,编译并生成一个…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第二集:通过InControl插件实现绑定玩家输入以及制作小骑士移动空闲动画

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、通过InControl插件实现绑定玩家输入二、制作小骑士移动和空闲动画 1.制作动画2.玩家移动和翻转图像3.状态机思想实现动画切换总结 前言 好久没来CSDN看看&…

利用JS数组根据数据生成柱形图

要求 <html> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document…

Python 基础 (标准库):datetime (基本日期和时间类型)

1. 官方文档 datetime --- 基本日期和时间类型 — Python 3.12.2 文档 tz — dateutil 3.9.0 documentation 2. 背景 2.1 处理时间数据的难点 计算机程序喜欢有序的、有规则的事件&#xff0c;但对于时间数据&#xff0c;其涉及的规则复杂且可能有变化&#xff0c;最典型例…

【homebrew安装】踩坑爬坑教程

homebrew官网&#xff0c;有安装教程提示&#xff0c;但是在实际安装时&#xff0c;由于待下载的包的尺寸过大&#xff0c;本地git缓存尺寸、超时时间的限制&#xff0c;会报如下错误&#xff1a; error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly&#xf…

2024永久激活版 Studio One 6 Pro for mac 音乐创作编辑软件 完美兼容

Studio One 6是一款功能强大的音乐制作软件&#xff0c;由PreSonus公司开发。它提供了全面的音频录制、编辑、混音和母带处理工具&#xff0c;适用于音乐制作人、音频工程师和创作人员。 Studio One 6拥有直观的用户界面&#xff0c;使用户能够快速而流畅地进行音乐创作。它采…

华为HarmonyOS地图服务 -- 如何实现地图呈现?-- HarmonyOS自学8

如何使用地图组件MapComponent和MapComponentController呈现地图&#xff0c;效果如下图所示。 MapComponent是地图组件&#xff0c;用于在您的页面中放置地图。MapComponentController是地图组件的主要功能入口类&#xff0c;用来操作地图&#xff0c;与地图有关的所有方法从此…

基于 onsemi NCV78343 NCV78964的汽车矩阵式大灯方案

一、方案描述 大联大世平集团针对汽车矩阵大灯&#xff0c;推出 基于 onsemi NCV78343 & NCV78964的汽车矩阵式大灯方案。 开发板搭载的主要器件有 onsemi 的 Matrix Controller NCV78343、LED Driver NCV78964、Motor Driver NCV70517、以及 NXP 的 MCU S32K344。 二、开…

【我的 PWN 学习手札】Fastbin Double Free

前言 Fastbin的Double Free实际上还是利用其特性产生UAF的效果&#xff0c;使得可以进行Fastbin Attack 一、Double Free double free&#xff0c;顾名思义&#xff0c;free两次。对于fastbin这种单链表的组织结构&#xff0c;会形成这样一个效果&#xff1a; 如果我们mallo…

记一次实战中对fastjson waf的绕过

最近遇到一个fastjson的站&#xff0c;很明显是有fastjson漏洞的&#xff0c;因为type这种字符&#xff0c;fastjson特征很明显的字符都被过滤了 于是开始了绕过之旅&#xff0c;顺便来学习一下如何waf 编码绕过 去网上搜索还是有绕过waf的文章&#xff0c;下面来分析一手&a…

分布式训练:(Pytorch)

分布式训练是将机器学习模型的训练过程分散到多个计算节点或设备上&#xff0c;以提高训练速度和效率&#xff0c;尤其是在处理大规模数据和模型时。分布式训练主要分为数据并行和模型并行两种主要策略&#xff1a; 1. 数据并行 (Data Parallelism) 数据并行是最常见的分布式…

【网络安全】逻辑漏洞之购买商品

未经授权,不得转载。 文章目录 正文正文 电子商务平台的核心功能,即购买商品功能。因为在这个场景下,任何功能错误都有可能对平台产生重大影响,特别是与商品价格和数量有关的问题。 将商品添加到购物车时拦截请求: 请求包的参数: 解码参数后,并没有发现价格相关的参数,…

Python(TensorFlow和PyTorch)及C++注意力网络导图

&#x1f3af;要点 谱图神经网络计算注意力分数对比图神经网络、卷积网络和图注意力网络药物靶标建模学习和预测相互作用腹侧和背侧皮质下结构手写字体字符序列文本识别组织病理学图像分析长短期记忆财务模式预测相关性生物医学图像特征学习和迭代纠正 Python注意力机制 对…