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,一经查实,立即删除!

相关文章

实践:修改正式站表名

一.引言 现在有一个需求&#xff0c;发现正式站的数据库里面有个表名不合理&#xff0c;需要修改&#xff0c;但是正式站一般不能修改表名&#xff0c;所以现在的做法是新建一个表&#xff0c;将旧表的数据复制到新表&#xff0c;然后将旧表删除。由于正式站的数据还在不断产生…

[Redis实战]优惠券秒杀

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

el-date-picker 日期选择限制

el-date-picker 日期选择限制 场景&#xff1a; 选择时间的区间是31天&#xff0c;默认显示最近一周的数据 代码&#xff1a; <el-date-picker v-model"due_date" type"daterange" range-separator"至" start-placeholder"开始日期&qu…

多线程多进程的使用场景和常见问题处理

多线程多进程的使用场景 多线程和多进程都是用来实现并发执行的技术&#xff0c;它们可以提高程序的性能和效率。它们各自适用于不同的场景&#xff1a; 多线程的使用场景&#xff1a; I/O密集型任务&#xff1a;当程序需要进行大量的I/O操作&#xff08;如文件读写、网络通…

【PythonRS】基于Python对栅格数据进行归一化(统一量纲至0~1)

有段时间没有更新Python处理栅格、矢量数据了&#xff0c;一部分是因为之前基本上已经把如何使用Python处理地理数据的方法覆盖完了&#xff0c;另一部分是因为最近有其他方面的知识需要学习和巩固。也是赶巧&#xff0c;最近有个项目需要构建模型对影像进行反演需要用到归一化…

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;这样你一打包所有的静态资源都会放到打包根目录下。但是此时你在项目中引用的地址一定要是…

OM6621选型指南详细对比应用蓝牙遥控智能穿戴游戏手柄

昂瑞微蓝牙OM6621系列对比选型指南 OM6621EM和OM6621ED性能特点 超低功耗蓝牙SOC精简BLE5.1协议栈主频64Mhz&#xff0c;40KB RAM集成红外线收发电路主要应用在语音遥控、鼠标、水表等 功能特点 功耗&#xff1a;1秒连接平均电流&#xff1a;11uA峰值电流&#xff1a;TX0dBm…

Vue 监听状态 watch 与监听状态 watchEffect

监听状态 watch watch 函数用于监听响应式数据的变化。 使用 watch 函数监听基于 ref 创建的响应式数据 (基本数据类型)。 import { ref, watch } from "vue" export default {setup() {const text ref("")watch(text, (current, previous) > {conso…

javascript2

文章目录 一、 内置对象1&#xff09; 对象2&#xff09; Array 数组1. 创建2. 特点3. 属性和方法4. 二维数组 3&#xff09;String 对象1. 创建2. 特点3. 属性4. 方法 4) Math 对象1. 定义2. 属性3. 方法 5&#xff09;日期对象1. 创建日期对象2. 日期对象方法 1. 创建日期对象…

结构体--高考数组

高考数组 高考数组 题意 求给定N组数组的平均值并输出最大的两个 思路 先求出其平均值再结构体进行比较&#xff0c;输出前两个最大值 算法一&#xff1a;结构体 实现步骤 定义结构体&#xff0c;进行平均值的比较&#xff0c;给数据定义下标&#xff0c;sort进行递增排序&a…

c++ 重写 重构 重载

特性重写&#xff08;Override&#xff09;重构&#xff08;Refactoring&#xff09;重载&#xff08;Overloading&#xff09;定义在派生类中提供与基类同名同参数的新方法实现改进代码内部结构但不改变外在行为在同一作用域内创建多个参数列表不同的同名函数目的实现多态&…

(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…

FreeBSD下安装Jenkins(软件测试集成工具)记录

简要介绍Jenkins 简而言之&#xff0c;Jenkins 是领先的开源自动化服务器。它使用 Java 构建&#xff0c;提供了 1,800 多个插件来支持几乎任何事情的自动化&#xff0c;因此人类可以将时间花在机器无法完成的事情上。 主要目的&#xff1a; 持续、自动地构建/测试软件项目。…