前端 + 接口请求实现 vue 动态路由

前端 + 接口请求实现 vue 动态路由

在 Vue 应用中,通过前端结合后端接口请求来实现动态路由是一种常见且有效的权限控制方案。这种方法允许前端根据用户的角色和权限,动态生成和加载路由,而不是在应用启动时就固定所有的路由配置。

实现原理

  1. 定义静态路由配置:

    • 在项目的初始阶段,定义一套完整的路由配置,这些配置包含了所有可能的路由路径和相关的权限信息。
  2. 用户登录与鉴权:

    • 用户登录时,前端向后端发送请求验证用户的身份。
    • 服务器验证成功后,返回一个包含用户信息和权限的数据对象。
  3. 获取用户权限信息:

    • 前端根据登录时获得的令牌(如 JWT),再次向后端请求获取当前用户的权限信息。
    • 权限信息可能包括用户的角色、能够访问的资源等。
  4. 动态生成路由:

    • 前端根据从后端获取的权限信息,动态生成符合用户权限的路由表。
    • 这个过程可以通过递归算法处理路由配置树,根据用户的权限过滤掉无权访问的路由。
  5. 动态添加路由:

    • 使用 Vue Router 的 router.addRoutes(routes) 方法将生成的路由动态添加到路由实例中。
    • 这样只有经过权限验证的路由才会被添加,从而实现了权限控制。
  6. 动态渲染菜单:

    • 左侧菜单通常是基于生成的路由表来渲染的,因此只有用户有权访问的路由才会在菜单中显示。

优点

  1. 安全性:

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

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

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

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

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

示例

在前导航路由钩子 beforeEach 函数里发送接口请求获取路由信息

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(() => {// 获取路由信息store.dispatch('GenerateRoutes').then((res) => {console.log('--------------', res);// 根据roles权限生成可访问的路由表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`) // 否则全部重定向到登录页}}
})

permission.js 文件需引入到 main.js 里

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

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

假设后端接口返回的路由权限如下

[{path: '/admin',meta: {title: "系统管理",},component: 'Layout',children: [{path: 'user',name: 'userIndex',meta: {title: "用户管理",},component: '/admin/user/index.vue'},{path: 'role',name: 'roleIndex',meta: {title: "角色管理",},component: '/admin/role/index.vue',children: [{path: 'add',name: 'addRole',meta: {title: "添加角色",},component: '/admin/user/index.vue'},{path: 'update',name: 'updateRole',meta: {title: "编辑角色",},component: '/admin/role/index.vue'}]}]},{path: '/tableEcho',meta: {title: "表格管理",},component: 'Layout',children: [{path: 'test',name: 'tableEchoIndex',meta: {title: "表格测试",},component: '/tableEcho/index.vue',children: [{path: 'add',name: 'addTable',hidden: true,meta: {title: "新增测试",},component: '/tableEcho/add.vue'}]},],},
]

vuex 处理数据

store/index.vue

// store/index.vue
import Vue from 'vue'
import Vuex from 'vuex'
import { routes, dynamicRoutes } from "@/router";
import { login, getInfo, logout, getRouters } from "@/api/user";
import { setStore, clearStore } from '@/utils/store';
import Layout from '@/Layout/index.vue'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']);}resolve();} else {reject(error);}}).catch(error => {reject(error);})})},GenerateRoutes({ commit }) {return new Promise((resolve, reject) => {// 向后端请求路由数据getRouters().then(res => {if (res.data.code === 0 || 200) {const sdata = JSON.parse(JSON.stringify(res.data.routes));console.log('sdata::: ', sdata);let newRouters = filterAsyncRouter(sdata);// 连接公共路由const sidebarRoutes = routes.concat([...newRouters]);commit('SET_ROUTE', sidebarRoutes);resolve(sidebarRoutes);} 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: {}
})const loadView = (view) => {// 路由懒加载return () => import(`@/views${view}`);
};function filterAsyncRouter(routes) {return routes.filter((route) => {if (Array.isArray(route.children) && route.children.length > 0) {// 如果该路由含有子路由时,递归调用该函数route.children = filterAsyncRouter(route.children);}if (route.component) {// Layout ParentView 组件特殊处理if (route.component === "Layout") {route.component = Layout;} else {// 路由组件懒加载route.component = loadView(route.component);}}return true;})
}

由于接口返回的 component 是字符串, 需手动封装函数转换成组件

公共路由如下

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 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},
]const router = new VueRouter({base: process.env.BASE_URL,routes
})export default router

文件结构如下
在这里插入图片描述
页面菜单渲染

在这里插入图片描述

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

前端单独实现动态路由参考连接: 前端单独实现 vue 动态路由

总结

通过以上步骤,你可以实现一个完整的动态路由权限管理系统:

  • 后端接口返回路由配置:获取用户的权限信息及路由信息。
  • 动态加载组件:使用异步组件方式加载指定路径的组件。
  • 动态添加路由:根据权限信息动态添加路由。
  • 路由守卫:使用 router.addRoutes()router.addRoute() 添加路由。

这样可以确保应用根据用户的权限动态加载相应的路由,增强安全性与灵活性。

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

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

相关文章

el-tree父子不互相关联时,手动实现全选、反选、子级全选、清空功能

el-tree父子不互相关联时,手动实现全选、反选、子级全选、清空功能 1、功能实现图示 2、实现思路 当属性check-strictly为true时,父子节点不互相关联,如果需要全部选中或选择某一节点下的全部节点就必须手动选择每个节点,十分麻…

【mysql】逻辑运算符

逻辑运算符 逻辑运算符主要是为了判断表达式的真假,返回结果也是1,0,null OR 这里面或就是两个条件或的关系,比如我要department_id等于10和等于20的情况就可以使用或. SELECT last_name,salary,department_id FROM employees WHERE department_id10 OR department_id20 …

CTF——简单的《WEB》

文章目录 一、WEB1、easysql2、baby_web3、baby_sql4、upload_easy5、easygame拓展1.1拓展1.2 6、ht_ssti7、包容乃大 一、WEB 1、easysql 题目描述: sql注入漏洞 1.常用的sql注入测试语句 2.sql注入bypass 解题思路 这边提示基本给的也很完整的,不…

C++开发基础之理解 CUDA 编译配置:`compute_XX` 和 `sm_XX` 的作用

前言 在 CUDA 编程中,确保代码能够在不同的 NVIDIA GPU 上高效运行是非常重要的。为了实现这一点,CUDA 编译器 (nvcc) 提供了多种配置选项,其中 compute_XX 和 sm_XX 是两个关键的编译选项。本文将深入探讨这两个选项的作用及其配置顺序&…

大一新生以此篇开启你的算法之路

各位大一计算机萌新们,你们好,本篇博客会带领大家进行算法入门,给各位大一萌新答疑解惑。博客文章略长,可根据自己的需要观看,在博客中会有给大一萌新问题的解答,请不要错过。 入门简介: 算法…

可信的人类与人工智能协作:基于人类反馈和物理知识的安全自主驾驶强化学习

可信的人类与人工智能协作:基于人类反馈和物理知识的安全自主驾驶强化学习 Abstract 在自动驾驶领域,开发安全且可信赖的自动驾驶策略仍然是一项重大挑战。近年来,结合人类反馈的强化学习(RLHF)因其提升训练安全性和…

中国银河资产笔试25届考什么?如何通过考试|附真题库面试攻略

嘿,各位小伙伴们!我是职小豚,今天就带大家一起探秘中国银河资产 25 届秋招,为大家揭开这场金融之旅的神秘面纱。 一、中国银河资产介绍 中国银河资产,那可是金融领域的璀璨巨星!它就像一座闪耀着智慧光芒…

unity安装配置和vs2022联动教程

目录 1.选择vs2022配置 2.安装unity 2.1安装unity hub 2.2注册个人账号 2.3安装编辑器 2.4修改为简体中文 2.5添加许可证 2.6安装位置修改 3.项目的创建 3.1如何创建 3.2如何选择 3.3配置语言 3.4去哪里找语言包 4.unity编辑器窗口的介绍 4.1游戏的运行和停止 4…

11、Hive+Spark数仓环境准备

1、 Hive安装部署 1)把hive-3.1.3.tar.gz上传到linux的/opt/software目录下 2)解压hive-3.1.3.tar.gz到/opt/module/目录下面 [shuidihadoop102 module]$ tar -zxvf /opt/software/hive-3.1.3.tar.gz -C /opt/module/ 3)修改hive-3.1.3-b…

《深度学习》深度学习 框架、流程解析、动态展示及推导

目录 一、深度学习 1、什么是深度学习 2、特点 3、神经网络构造 1)单层神经元 • 推导 • 示例 2)多层神经网络 3)小结 4、感知器 神经网络的本质 5、多层感知器 6、动态图像示例 1)一个神经元 相当于下列状态&…

安卓开发板_联发科MTK开发评估套件串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发,USB 串口适配器( USB 转串口 TTL 适配器的简称)对于检查系统启动日志非常有用,特别是在没有图形桌面显示的情况下。 1.选购适配器 常用的许多 USB 转串口的适配器&#xf…

通过nginx代理转发实现共用80和443端口

目录 项目场景: 问题:怎么实现端口共用? 一、域名解析到nginx服务器 二、创建vhost解析到实际的服务器 三、防火墙的配置 项目场景: 公司自建服务器,有一条专线带有公网IP,如何满足不同的域名解析共用…

go-map系统学习

map底层结构 Goland的map的底层结构使用hash实现,一个hash表里有多个hash表节点,即bucket,每个bucket保存了map中的一个或者一组键值对。 map结构定义: runtime/map.go:hmap type hmap struct {// Note: the format of the hma…

win10 安装多个版本的python

1,安装python3.9 和python3.10 2, 安装完之后分别打开两个版本的Python的安装目录(第一层目录),把pythonw.exe分别重命名为pythonw_39.exe和pythonw_310.exe,把python.exe复制一份,并分别重命名为python_…

【C++】_stack和_queue容器适配器、_deque

当别人都在关注你飞的有多高的时候,只有父母在关心你飞的累不累。💓💓💓 目录 ✨说在前面 🍋知识点一:stack •🌰1.stack介绍 •🌰2.stack的基本操作 🍋知识点二&…

怎么利用XML发送物流快递通知短信

现如今短信平台越来越普遍了,而短信通知也分很多种,例如服务通知、订单通知、交易短信通知、会议通知等。而短信平台在物流行业通知这一块作用也很大。在家时:我们平时快递到了,如果电话联系不到本人,就会放到代收点,然…

力扣之1777.每家商店的产品价格

文章目录 1. 1777.每家商店的产品价格1.1 题干1.2 建表1.3 题解1.4 结果截图 1. 1777.每家商店的产品价格 1.1 题干 表:Products -------------------- | Column Name | Type | -------------------- | product_id | int | | store | enum | | price | int | ---…

分布式调度方案:Elastic-Job

文章目录 一、什么是分布式调度二、Elastic-Job 介绍三、Elastic-Job 实战3.1 环境搭建3.1.1 本地部署3.1.2 服务器部署3.1.3 Zookeeper 管控台界面 3.2 入门案例3.3 SpringBoot 集成 Elastic-Job3.4 任务分片(★)3.5 Dataflow 类型调度任务 一、什么是分…

提升LLM能力表现的四种AI代理策略

欢迎来到AI产品经理从0到1研习之旅。 “AI Agent”系列的第二篇,它来了!我说过,AI Agent才是我最看好的(大模型落地应用)未来,嘿嘿,目测有很多大佬与我有相似的看法。 本文从吴恩达&#xff0…

Python | Leetcode Python题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; class Solution:def readBinaryWatch(self, turnedOn: int) -> List[str]:ans list()for i in range(1024):h, m i >> 6, i & 0x3f # 用位运算取出高 4 位和低 6 位if h < 12 and m < 60 and bin(i).count("1&…