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://xiahunao.cn/news/2659568.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【本地缓存篇】如何实现本地缓存?

如何实现本地缓存? ✔️典型解析✔️数据结构✔️线程安全✔️对象上限✔️清除策略✔️过期时间 ✔️扩展知识仓基于Caffeine实现本地缓存 ✔️典型解析 所谓本地缓存&#xff0c;就是和应用服务器在一起的缓存工具&#xff0c;将需要缓存的数据放到本地缓存中&#xff0c;可…

YOLOv5-Lite 树莓派4B 15帧教程

【前言】 由于v5Lite仓库遗漏了不少历史问题&#xff0c;最大的问题是毕业后卷起来了&#xff0c;找不到时间更新。 上面是这篇博客的背景&#xff0c;那么先说下结论&#xff0c;使用 v5lite-e 模型&#xff0c;在 树莓派4B&#xff08;4G内存&#xff09; 上&#xff0c;有三…

Unity 爱心血量效果

这里写自定义目录标题 1.准备爱心血条2.HeartUI 代码3.在Inspector窗口中绑定好对象4.在血量减少的地方&#xff0c;调用更新方法5.效果展示 1.准备爱心血条 准备好红色爱心和灰色爱心的图片 2.HeartUI 代码 using System.Collections; using System.Collections.Generic; u…

【AI】计算机视觉VIT文章(Transformer)源码解析

论文&#xff1a;Dosovitskiy A, Beyer L, Kolesnikov A, et al. An image is worth 16x16 words: Transformers for image recognition at scale[J]. arXiv preprint arXiv:2010.11929, 2020 源码的Pytorch版&#xff1a;https://github.com/lucidrains/vit-pytorch 0.前言 …

Erlang、RabbitMQ下载与安装教程(windows超详细)

目录 安装Erlang 1.首先安装RabbitMQ需要安装Erlang环境 2.点击下载好的.exe文件进行傻瓜式安装,一直next即可 3.配置Erlang环境变量 安装RabbitMQ 1.给出RabbitMQ官网下载址&#xff1a;Installing on Windows — RabbitMQ&#xff0c;找到 2.配置RabbitMQ环境变量&#xff0…

软件测试/测试开发丨Pytest学习笔记

Pytest 格式要求 文件: 以 test_ 开头或以 _test 结尾类: 以 Test 开头方法/函数: 以 _test 开头测试类中不可以添加构造函数, 若添加构造函数将导致Pytest无法识别类下的测试方法 断言 与Unittest不同, 在Pytest中我们需要使用python自带的 assert 关键字进行断言 assert…

【强化学习】基于蒙特卡洛MC与时序差分TD的简易21点游戏应用

1. 本文将强化学习方法&#xff08;MC、Sarsa、Q learning&#xff09;应用于“S21点的简单纸牌游戏”。 类似于Sutton和Barto的21点游戏示例&#xff0c;但请注意&#xff0c;纸牌游戏的规则是不同且非标准的。 2. 为方便描述&#xff0c;过程使用代码截图&#xff0c;文末附链…

【k8s源码分析-Apiserver-2】kube-apiserver 结构概览以及主体部分源码分析

参考 Kubernetes 源码剖析&#xff08;书籍&#xff09;kube-apiserver的设计与实现 - 自记小屋 kube-apiserver 核心思想 APIGroupInfo 记录 GVK 与 Storage 的对应关系 将 GVK 转换成&#xff0c;Restful HTTP Path将 Storage 封装成 HTTP Handler将上面两个形成映射&#…

CSP CCF 201409-2 画图 C++满分题解

解题思路&#xff1a; 1.使用二维数组标记每一个方块是否被涂色。 2.注意坐标代表的是点&#xff0c;不是方块&#xff0c;交界处的坐标只能算一个方块。 3.可以看成&#xff1a;每一个坐标都对应它左上角的一个小方块&#xff0c;这样可以避免重复计算方块数 #include<i…

3 个月前被裁员了,心情跌落谷底,直到我看到了这本神书…

3个月前的某一天&#xff0c;正在愉快的打工&#xff0c;突然被喊去谈话&#xff0c;然后就被辞退了。。 加入了找工作的大军 然而&#xff0c;因为疫情&#xff0c;因为大专学历的我&#xff0c;找工作比以往都艰难了许多 很多&#xff0c;纯粹就是因为学历&#xff0c;都不…

第十三届蓝桥杯大赛青少年国赛C++组编程题真题(2022年)

第十三届蓝桥杯大赛青少年国赛C组编程题真题&#xff08;2022年&#xff09; 编程题第 1 题 问答题 电线上的小鸟 题目描述&#xff1a; 在一根电线上落有N只小鸟&#xff0c;有的小鸟头向左看&#xff0c;有的小鸟头向右看&#xff0c;且每只小鸟只能看到它视线前的那一只小…

Java之ThreadLocal 详解

ThreadLocal 详解 原文地址&#xff1a;https://juejin.cn/post/6844904151567040519open in new window。 什么是ThreadLocal&#xff1f; ThreadLocal提供线程局部变量。这些变量与正常的变量不同&#xff0c;因为每一个线程在访问ThreadLocal实例的时候&#xff08;通过其…

机器学习之人工神经网络(Artificial Neural Networks,ANN)

人工神经网络(Artificial Neural Networks,ANN)是机器学习中的一种模型,灵感来源于人脑的神经网络结构。它由神经元(或称为节点)构成的层级结构组成,每个神经元接收输入并生成输出,这些输入和输出通过权重进行连接。 人工神经网络(ANN)是一种模仿生物神经系统构建的…

电气产品外壳常用材质PA、PC、PBT、ABS究竟是什么?

在如今工业制造领域&#xff0c;各种改性塑料、复合材料以及轻质合金材料的运用日趋成熟。在电气领域&#xff0c;不同电气产品的外壳、组件材质采用不同材料&#xff0c;以同为科技&#xff08;TOWE&#xff09;电气产品为例&#xff0c;工业连接器系列产品采用PA6外壳材质、机…

软件测试/测试开发丨Python常用数据结构学习笔记

Python常用数据结构 list 列表 列表定义 列表是有序的可变元素的集合&#xff0c;使用中括号[]包围&#xff0c;元素之间用逗号分隔列表是动态的&#xff0c;可以随时扩展和收缩列表是异构的&#xff0c;可以同时存放不同类型的对象列表中允许出现重复元素 列表使用&#x…

鸿蒙 DevEco Studio 3.1 入门指南

本文主要记录开发者入门&#xff0c;从软件安装到项目运行&#xff0c;以及后续的学习 1&#xff0c;配置开发环境 1.1 下载安装包 1.2 下载SDK及工具链 1.3 诊断开发环境 1.4 配置环境变量 配置HDC工具环境变量 配置Node环境变量 2.创建和运行 2.1 新建项目 2.2 页面…

掌握激活函数(一):深度学习的成功之源

文章目录 引言基本概念常用激活函数举例Sigmoid激活函数公式Sigmoid函数的数学特性示例基于NumPy和PyTorch实现Sigmoid函数将Sigmoid函数应用于二分类任务 Sigmoid激活函数的局限性举例 ReLU激活函数公式ReLU函数的数学特性ReLU函数的特点示例基于NumPy和PyTorch实现ReLU函数搭…

虚析构和纯虚析构

多态使用时&#xff0c;如果子类中有属性开辟到堆区&#xff0c;那么父类的指针在释放时无法调用到子类的析构代码 解决方式&#xff1a;将父类中的析构代码函数改为虚析构或者纯虚析构 虚析构和纯虚析构共性&#xff1a; 可以解决父类指针释放子类对象 都需要有具体的函数…

vu3-14

第一个需求是在用户登录成功之后&#xff0c;在主页显示用户的真实姓名和性别&#xff0c;这些信息要调用后端API获取数据库里面的信息&#xff0c;第二个需求是点击菜单1&#xff0c;在表单中修改用户信息之后&#xff0c;更新到后端数据库&#xff0c;然后在主页同步更新用户…

软件测试/测试开发丨接口测试学习笔记分享

一、Mock 测试 1、Mock 测试的场景 前后端数据交互第三方系统数据交互硬件设备解耦 2、Mock 测试的价值与意义 不依赖第三方数据节省工作量节省联调 3、Mock 核心要素 匹配规则&#xff1a;mock的接口&#xff0c;改哪些接口&#xff0c;接口哪里的数据模拟响应 4、mock实…