vue3 根据用户权限控制左侧菜单和路由拦截

目录

前言

整体思路

详细开发

1.左侧菜单的显隐控制

2.控制路由权限

补充权限控制

总结


前言

我这里是vue3开发的一个后台管理系统,所以涉及用户权限管理,以及页面权限等,其他模块部分可以查看专栏,这里只对怎么实现根据用户权限控制左侧菜单和路由。

        用户权限控制路由表和左侧菜单一般是带权限的系统的通用功能,在我还在上大学的时候,实现这个功能感觉很费劲,因为那时候对Vue一知半解,很多权限模模糊糊地实现了,实际上我现在也不能说全部了解,但是经过了几个项目,脑子里有了大概的思路。     

整体思路

用户权限我认为大可以分成两类:控制访问哪些页面,控制能执行哪些操作。

        前者要实现用户只能查看有权限的页面,比如,左侧菜单的菜单栏都是根据权限过滤的,跳转路由时要判断权限(路由守卫的工作),后者要做到用户只能操作有权限的功能,比如点击新建、修改按钮等。后者实现也很简单,好的方法是加一个权限的指令,哪个地方需要判断权限就调用,这里先只讨论前者的实现。

        如何实现这个功能,拆解为两部分,首先我们要知道用户有哪些菜单的权限,通常是通过接口获取,接着我们要知道用户有哪些路由的权限,比如'/login','/401','/user',这些页面,当然有时候401,500这些页面也会配置在白名单里,不受权限管理。

总结有两部分的权限:菜单权限,路由权限。

这里实现的功能如下:

用户有菜单A,B,C的权限,那么左侧菜单就显示ABC,

然后用户有路由 '/A','/B','/C','/D','/401','/login' 的权限,

那么用户可以访问这几个页面。

假如,用户没有'/B'的权限,那么点击菜单B也会跳到401页面。

  具体的功能明白了,那就开始详细的开发吧。

详细开发

我先说下我具体的思路。

        首先用户需要获取两部分权限,加上这两个接口,在哪里调用呢,肯定是左侧菜单渲染前,路由表生成前。

        前者应该在sidemenu的组件页进行,一般我们都会有这个页面对吧,根据获取到的左侧菜单,进行渲染,这样就完成了第一部分工作。

        至于路由权限的控制,那么肯定需要进行路由守卫的帮助,所以一般我会在router下进行,添加路由守卫拦截,每次router.push的时候,判断一下是否有权限。至于权限从哪里来,我这里是从用户的信息里获取到的,一般系统会有获取用户信息的接口吧,这里会有一些用户的个人配置和基本信息。

        我是在登录后,获取用户信息,存在了store里,然后nvabar里也获取了用户信息,执行同样的操作,然后这样就避免了每次刷新之后信息丢失的问题,当然用户信息可以放在storage或者cookie里,但是为了信息安全,还是别这样做了。

1.左侧菜单的显隐控制

左侧菜单的显隐我是根据权限显示的,没有权限则,没有这个菜单项,这样比较简单。

我显示一下我的数据格式,我获取到的菜单权限是一个数组,每一个对象里有对应路由的id和名字,我需要根据权限显示对应的菜单,如下:

 

这里是全部的路由表的数据:

 

其中我将需要权限的页面都放在了path='/'的children里。 

下面我实现的具体代码吧

const routes = ref()
onMounted(async () => {await getPermissionRoutes()routes.value = handleAdminMenus()
})const handleAdminMenus = () => {const filteredRoutes = allRoutes.value.reduce((acc, route) => {if (route.path == '/') {const filteredChildren = route.children.filter((child) => {return adminMenus.value.some((item) => item.table_id === child.name)})if (filteredChildren.length > 0) {acc.push({ ...route, children: filteredChildren })}}return acc}, [])return filteredRoutes[0].children
}let adminMenus = ref()
const getPermissionRoutes = async () => {await getAdminMenus().then((res) => {adminMenus.value = resconsole.log(adminMenus.value, res)return adminMenus.value})
}
<template><div id="Sidebar" class="reset-menu-style"><!--logo--><Logo v-if="settings.sidebarLogo" :collapse="!sidebar.opened" /><!--router menu--><el-scrollbar><el-menuclass="el-menu-vertical":collapse="!sidebar.opened":default-active="activeMenu":collapse-transition="false"mode="vertical"><sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" /></el-menu></el-scrollbar></div>
</template>

我上面主要是将获取到权限菜单和路由表做了一个对应比较,只让菜单显示有权限的路由,那么自然也只能点击有权限的路由了,比如菜单ABC,当然如果输入路径‘/404’,此刻也是可以跳转的,如何后面控制了路由守卫,如果没有404的页面权限,那么输入404 ,应该会跳到401页面,这里先说明一下。

接下来我们就实现这部分功能——只能根据权限进入相应的页面

2.控制路由权限

这里的还是比较简单的,主要就以下一些功能

当然这是比较简单的一个拦截,如果需要做一些其他操作比如,生成动态路由,或者添加权限等,都可以在【判断是否有当前权限】这之后进行。

import router from '@/router'
import { progressClose, progressStart } from '@/hooks/use-permission'
import { useBasicStore } from '@/store/basic'
import { langTitle } from '@/hooks/use-common'
import settings from '@/settings'
import { Message } from '@/hooks/use-element'
import { userInfoReq } from '@/api/user'
//路由进入前拦截
//to:将要进入的页面 vue-router4.0 不推荐使用next()
const whiteList = ['/login', '/404', '/401', '/loading'] // no redirect whitelist
router.beforeEach(async (to) => {progressStart()document.title = langTitle(to.meta?.title) // i18 page titleconst basicStore = useBasicStore()// not loginif (!settings.isNeedLogin) {// basicStore.setFilterAsyncRoutes()return true}if (whiteList.indexOf(to.path) !== -1) {return true}// 是否已登录if (basicStore.token) {// 已登录if (to.path === '/login') {return '/'} else {const isGetUserInfo = basicStore.getUserInfo// 是否已获取用户信息和权限if (isGetUserInfo) {// 判断权限const userPermissions = basicStore.userPermissions || []const hasPermission = userPermissions.some((permission) => permission === to.path)if (hasPermission) {// basicStore.setFilterAsyncRoutes()return true} else {// 没有权限,跳转到 401 页面return '/401'}} else {try {// 获取用户信息和权限userInfoReq().then((res) => {basicStore.setUserInfo({userInfo: res})// 判断权限const userPermissions = basicStore.userPermissions || []const hasPermission = userPermissions.some((permission) => permission === to.path)if (hasPermission) {// basicStore.setFilterAsyncRoutes()return true} else {// 没有权限,跳转到 401 页面return '/401'}}).catch((err) => {basicStore.setUserInfo({userInfo: {}})Message(err || '获取用户信息失败', 'error')return '/401'})} catch (err) {console.log('permission-catch-error', err)return `/login?redirect=${to.path}`}}}} else {// 未登录,跳转到登录页面if (!whiteList.includes(to.path)) {return `/login?redirect=${to.path}`} else {return true}}
})
//路由进入后拦截
router.afterEach(() => {progressClose()
})

这里留了一个口子,basicStore.setFilterAsyncRoutes(),如果登录后需要生成动态路由,则可以在这里进行。,但是我们的路由比较简单,就不做这些啦。

补充权限控制

这里补充一些登录后的路由跳转到权限控制

系统里我写了一个loading页面,系统登录后先跳到这个页面,这个页面会获取当前用户的菜单权限和用户权限,这个页面的作用如下:

如果当前菜单首页,那么这时会跳至首页。

如果当前有权限的菜单有BCD里没有默认跳转页面,那么就根据当前的菜单顺序,跳转至第一个菜单页,那么会去往B页面。

所以这个页面的目的也很明显啦,就是一个中转页,如果系统不小心去往了401,404,那么返回首页的时候,这个首页也会去往loading,这样就可以在用户没有首页权限时,也能有一个当前权限的【首页】,总要给用户一个页面的去处嘛。 

这些功能的实现也很简单,如下:

let adminMenus = ref()
onMounted(async () => {if (basicStore.adminMenus?.length > 0) {adminMenus.value = basicStore.adminMenusgoToPath()} else {await getPermissionRoutes()goToPath()}
})const goToPath = () => {const userPermissions = basicStore.userPermissions || []const hasPermission = userPermissions.some((permission) => permission === adminMenus.value[0].table_id)if (userPermissions.some((permission) => permission === '/homePage')) {router.push(`/homePage`)} else {if (adminMenus.value[0].type === 'page' && hasPermission) {router.push(adminMenus.value[0].table_id)} else if (hasPermission) {router.push(`/t${adminMenus.value[0].table_id}`)}}
}

总结

其实权限控制一般都跟业务关联很强,经常会在基础的上面那种权限控制上添加新的,所以很多时候在后期加新需求时,哪怕对权限很熟悉,也会有时候遇到一些bug,只要熟悉每一个组件的加载顺序,多加打印,就能慢慢解决~

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

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

相关文章

[Redis实战]优惠券秒杀

三、优惠券秒杀 3.1 全局唯一ID 每个店铺都可以发布优惠券&#xff1a; 当用户抢购时&#xff0c;就会生成订单并保存到tb_voucher_order这种表中&#xff0c;而订单表如果使用数据库自增ID就存在一些问题&#xff1a; id的规律性太明显受单表数据量的限制 场景分析一&…

Word2Vec详解: CBOW Skip-gram和负采样

Word2Vec&#xff1a; CBOW & Skip-gram 如果是拿一个词语的上下文作为输入&#xff0c;来预测这个词语本身&#xff0c;则是 CBOW 模型。 而如果是用一个词语作为输入&#xff0c;来预测它周围的上下文&#xff0c;那这个模型叫做 Skip-gram 模型。 CBOW 模型 连续词袋模…

简单实现一个自定义loader

webpack定义的loader需要遵循单一功能原则&#xff0c;也就是一个loader只实现一个功能。在实现开发中&#xff0c;我们会直接使用诸如蓝湖等生成的样式&#xff0c;比如 button{background: rgb(255, 85, 46); }但为了考虑主题换肤&#xff0c;我们实现的想要的可能是 butto…

在用Vite开发时静态图片放哪里,才能保证显示,不出现找不到资源

在用Vite开发时静态图片放哪里 在用Vite开发时静态图片&#xff08;资源&#xff09;放哪里呢 &#xff1f; 如果你想直接全部显示的那么请你把静态资源放到public目录下面&#xff0c;这样你一打包所有的静态资源都会放到打包根目录下。但是此时你在项目中引用的地址一定要是…

(2)llvm解析器和抽象语法树

解析器的输出是抽象语法树 对于数字字面量&#xff0c;创造了一个实例&#xff0c;并捕捉 变量捕捉函数名&#xff1b;二元表达式捕捉运算符&#xff1b;函数调用捕捉函数名和函数调用参数 函数原型和函数定义 构建语法树 getNextToken会从输入流里拿一个token&#xff0c;Cur…

克魔助手:方便查看iPhone应用实时日志和奔溃日志工具

查看ios app运行日志 摘要 本文介绍了一款名为克魔助手的iOS应用日志查看工具&#xff0c;该工具可以方便地查看iPhone设备上应用和系统运行时的实时日志和奔溃日志。同时还提供了奔溃日志分析查看模块&#xff0c;可以对苹果奔溃日志进行符号化、格式化和分析&#xff0c;极…

MSVC++ 编译 module std

环境&#xff1a;windows 10 19045.xxxx 只安装了MSVC C 工具链和一个版本的SDK&#xff0c;SDK版本建议选一个和本机系统匹配的。 cd %USERPROFILE%\source\repos\STLModules mkdir x86 mkdir x64 打开“x86 Native Tools Command Prompt for VS 2022”控制台&#xff0c;…

【mac-m1 docker 安装upload-labs靶场】

1.搜索upload-labs docker search upload-labs 2.下载upload-labs docker pull c0ny1/upload-labs 3.启动 docker run -it -d --name uploadlabs -p 80:80 c0ny1/upload-labs --platform linux/amd64 4.访问127.0.0.1:80

java设计模式学习之【备忘录模式】

文章目录 引言备忘录模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用备忘录示例代码地址 引言 想象一下&#xff0c;你正在编辑一篇重要的文档&#xff0c;突然你意识到最近的一些更改实际上破坏了文档的结构。幸运的是&#xff0c;你的文本编辑器允许你撤…

哈希桶的模拟实现【C++】

文章目录 哈希冲突解决闭散列 &#xff08;开放定址法&#xff09;开散列 &#xff08;链地址法、哈希桶&#xff09;开散列实现&#xff08;哈希桶&#xff09;哈希表的结构InsertFindErase 哈希冲突解决 闭散列 &#xff08;开放定址法&#xff09; 发生哈希冲突时&#xf…

【MyBatis-Plus】进阶之乐观锁、悲观锁逻辑删除分页和查询构造器的使用

目录 一、乐观锁、悲观锁 1、什么是乐观锁和悲观锁 ①乐观锁&#xff08;Optimistic Locking&#xff09;: ②悲观锁&#xff08;Pessimistic Locking&#xff09;: ③实现方式 2、乐观锁和悲观锁的区别 ①乐观锁&#xff08;Optimistic Locking&#xff09; ②悲观锁&…

【CSAPP】探究BombLab奥秘:Phase_2的解密与实战

&#x1f4cb; 前言 ​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《斯坦福大学之CSAPP》⏰诗赋清音&#xff1a;桃花灼灼春风暖&#xff0c;心随乐曲扬徐徐。 苦尽甘来梦未阑&#xff0c;岁月长河任舟游。 ​ &#x1f389;欢迎…

消息队列基础知识

学一点&#xff0c;整一点&#xff0c;基本都是综合别人的&#xff0c;弄成我能理解的内容 https://blog.csdn.net/BenJamin_Blue/article/details/125946812 https://blog.csdn.net/qq_46119575/article/details/129794304 &#x1f4cc;导航小助手&#x1f4cc; 生产者-消费者…

【C语言】初识C语言

本章节主要目的是基本了解C语言的基础知识&#xff0c;对C语言有一个大概的认识。 什么是C语言 在日常生活中&#xff0c;语言就是一种人与人之间沟通的工具&#xff0c;像汉语&#xff0c;英语&#xff0c;法语……等。而人与计算机之间交流沟通的工具则被称为计算机语言&am…

Redis中RDB和AOF

Redis中RDB和AOF 定时间间隔执行数据集的时间快照&#xff0c;把某一时刻数据和妆容以文件的形式写到磁盘上&#xff0c;也就是快照。 配置文件 如果是普通安装方式可以跳过&#xff0c;如果是docker安装&#xff0c;需要到官网下载redis.conf配置文件到本地&#xff0c;地址…

单挑力扣(LeetCode)SQL题:1951. 查询具有最多共同关注者的所有两两结对组(难度:中等)

题目&#xff1a;1951. 查询具有最多共同关注者的所有两两结对组 &#xff08;通过次数2,464 | 提交次数3,656&#xff0c;通过率67.40%&#xff09; 表: Relations ------------------- | Column Name | Type | ------------------- | user_id | int | | follower_id |…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理

深入浅出图解C#堆与栈 C# HeapingVS Stacking第二节 栈基本工作原理 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理](https://mp.cs…

非线性最小二乘问题的数值方法 —— 从牛顿迭代法到高斯-牛顿法 (II)

Title: 非线性最小二乘问题的数值方法 —— 从牛顿迭代法到高斯-牛顿法 (II) 姊妹博文 非线性最小二乘问题的数值方法 —— 从牛顿迭代法到高斯-牛顿法 (I) 非线性最小二乘问题的数值方法 —— 从牛顿迭代法到高斯-牛顿法 (II) ⟵ \longleftarrow ⟵ 本篇 非线性最小二乘问题的…

uniapp Vue3 面包屑导航 带动态样式

上干货 <template><view class"bei"><view class"container"><view class"indicator"></view><!-- 遍历路由列表 --><view v-for"(item, index) in routes" :key"index" :class&quo…