企业级Vue路由角色权限应该怎么做?
角色权限
角色权限,简单来说就是登录的用户能看到系统的哪些页面,不能看到系统的哪些页面。一般是后台管理系统才会涉及到如此复杂的角色权限。
对于 vue 技术栈,实现角色权限一般有两种方式。
第一种是利用 beforeEach 全局前置守卫。
第二种是利用 addRoutes 方法。
我们先来看看第一种
通过 beforeEach 实时监测用户权限
这种方案的核心就是首先在 routes 里面事先定义好路由的权限,然后在 beforeEach 全局前置守卫里面进行权限逻辑判断。看用户所拥有的角色和我们配置在路由里面的 roles 是否相匹配。匹配则允许进入,不匹配则重定向到无权限提示页面。
定义 routes
首先我们定义好系统的路由,对于非首页,我们一般都会使用路由懒加载。
在 meta 里面可以定义我们需要的元数据,这里需要加上我们路由的角色roles。也就是说进入该路由用户所需要具备的角色权限。如果没定义则代表任意角色都能进入。
js
复制代码
// router/routes.jsimport Home from "../views/Home.vue";const routes = [{path: "/",name: "Home",component: Home,meta: {needLogin: false, // 不需要登录title: "首页",},},{path: "/about", name: "About",component: () =>import(/* webpackChunkName: "about" */ "../views/About.vue"), // 路由懒加载meta: {needLogin: true, // 需要登录title: "关于",roles: ["admin", "manage"], // 该页面只有admin和普通管理员才能进入},},{path: "/nopermission", // 没权限就进入该页面name: "NoPermission",component: () =>import(/* webpackChunkName: "nopermission" */ "../views/NoPermission.vue"), // 路由懒加载meta: {needLogin: true, // 需要登录title: "暂无权限",},},{path: "/userlist", name: "UserList",component: () =>import(/* webpackChunkName: "userlist" */ "../views/UserList.vue"), // 路由懒加载meta: {needLogin: true, // 需要登录title: "用户管理",roles: ["admin"], // 该页面只有admin才能进入},},{path: "/login",name: "Login",component: () =>import(/* webpackChunkName: "login" */ "../views/Login.vue"), // 路由懒加载meta: {needLogin: false, // 不需要登录title: "登录",},},
];export default routes;
实例化 Router
然后创建路由,vue2和vue3创建路由的时候稍有区别,但是在路由鉴权那块是通用的。
js
复制代码
// router/index.js// vue2 写法
import VueRouter from "vue-router";
import routes from "./routes"const router = new VueRouter({mode: "history",base: process.env.BASE_URL,routes,
});// vue3 写法
import { createRouter, createWebHistory } from "vue-router";
import routes from "./routes"const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes,
});
定义路由拦截鉴权逻辑
创建好路由后,我们可以来定义路由拦截的逻辑了,主要通过 beforeEach 全局前置守卫。因为只要页面发生跳转都会进入 beforeEach 全局前置守卫。
这里的核心逻辑就是判断前往的页面是否需要登录,需要登录就进一步判断当前系统是否有token,没有token则重定向到登录页
如果有token,则进一步判断是否有用户信息,如果没有用户信息就获取用户信息
有了用户信息后再判断进入页面需要的角色是否和用户信息里面的角色相匹配,匹配则进入页面,不匹配则进入系统的无权限提示页面。
js
复制代码
// router/index.js// vue2和vue3通用
router.beforeEach(async (to, from, next) => {// 如果需要登录if (to.meta.needLogin) {// 获取tokenconst token = localStorage.getItem("token");// 如果有token 则直接放行if (token) {// 获取用户信息,从store里面获取let userInfo = store.getters["getUserInfo"];// 如果没有用户信息就获取用户信息if (!userInfo) {userInfo = await store.dispatch("getUserInfoAction");}// 如果页面需要权限,并且用户角色不满足则去无权限提示页if (to.meta.roles && !to.meta.roles.includes(userInfo.role)) {return next("/nopermission");}next();} else {// 否则去登录页next("/login");}} else {// 不需要登录则直接放行next();}
});// 修改标题的工作可以放在全局后置守卫
router.afterEach((to, from) => {if (to.meta.title) {document.title = to.meta.title;}
});
我们再来看看 store 的逻辑
js
复制代码
import { createStore } from "vuex";export default createStore({state: {userInfo: null,},getters: {getUserInfo: (state) => state.userInfo,},mutations: {setUserInfo(state, payload) {state.userInfo = payload;},},actions: {async getUserInfoAction({ commit }) {// 模拟后端获取用户信息的apiconst getUserInfoApi = () => {return Promise.resolve({ role: "manage", name: "jack" }); // 假设角色为 manage};const userInfo = await getUserInfoApi();commit("setUserInfo", userInfo);return userInfo;},},
});
使用
创建完路由后需要在 main.js 导入使用
js
复制代码
import router from "./router";// vue2写法
new Vue({router,render: (h) => h(App),
}).$mount("#app");// vue3写法
const app = createApp(App);
app.use(router).mount("#app");
在没登录的情况下,去about和userlist页面都会重定向到登录页。
给本地添加token 模拟登录完成后,因为用户信息角色是 manage,所以去 about 是没问题的,去 userlist 则会重定向到没有权限页面。
总结
优点:
实现简单,路由的权限在 meta 的 roles 里面配置。用户信息里面只需要包含当前用户的角色。
缺点:
系统所拥有的角色必须事先知道,然后以死代码的形式配置在 routes 里面,不支持动态添加角色。适用于小型角色固定的系统。
通过 addRoutes 动态添加路由
这种方案的核心就是调用后端接口,返回当前用户角色所拥有的菜单,格式化成路由后通过 addRoutes 将这些路由动态添加到系统。
定义 routes
因为路由从后端获取,所以这里我们只需要定义基本通用路由,也就是不涉及到权限的路由。比如登录页、没权限页、首页。
js
复制代码
import Home from "../views/Home.vue";const routes = [{path: "/",name: "Home",component: Home,meta: {title: "首页",},},{path: "/nopermission", // 没权限就进入该页面name: "NoPermission",component: () =>import(/* webpackChunkName: "nopermission" */ "../views/NoPermission.vue"), // 路由懒加载meta: {title: "暂无权限",},},{path: "/login",name: "Login",component: () =>import(/* webpackChunkName: "login" */ "../views/Login.vue"), // 路由懒加载meta: {title: "登录",},},
];export default routes;
实例化 Router
然后创建路由,vue2和vue3创建路由的时候稍有区别。
js
复制代码
// router/index.js// vue2 写法
import VueRouter from "vue-router";
import routes from "./routes"const router = new VueRouter({mode: "history",base: process.env.BASE_URL,routes,
});// vue3 写法
import { createRouter, createWebHistory } from "vue-router";
import routes from "./routes"const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes,
});
定义路由拦截鉴权逻辑
创建好路由后,我们就可以定义动态添加路由的逻辑啦。
这里的核心逻辑就是判断前往的页面是否在白名单内,不在白名单就进一步判断当前系统是否有token,没有token则重定向到登录页
如果有token,则进一步判断是否有用户菜单信息,如果没有用户菜单信息就获取用户菜单信息
有了用户菜单信息后,就先将菜单信息转换成前端路由需要的格式,然后通过addRoutes方法将路由动态添加到系统。
当用户前往不存在的路由时,路由的 name 属性会是
undefined,因此就重定向到无权限提示页面
js
复制代码
// router/index.js// vite创建的项目 使用这种方法实现动态加载
const modules = import.meta.glob("../views/*.vue");// 转换成 vue-router 需要的格式
const transformRoute = (menus) => {return menus.map((menu) => {return {path: menu.path,name: menu.name,component: modules[menu.component],meta: {title: menu.title,},};});
};// vue3需要手动实现该方法
const addRoutes = (routes) => {routes.forEach((route) => {router.addRoute(route);});
};// 白名单页面,不需要权限
const whiteLists = ["/login", "/"];router.beforeEach(async (to, from, next) => {// 是否是白名单if (!whiteLists.includes(to.path)) {// 获取tokenconst token = localStorage.getItem("token");// 如果有tokenif (token) {// 获取用户菜单信息,从store里面获取let userMenus = store.getters["getUserMenus"];// 如果没有用户菜单信息就获取用户菜单信息if (!userMenus) {userMenus = await store.dispatch("getUserMenuAction");// 菜单转成路由const userRoute = transformRoute(userMenus);// 动态添加路由 vue2 和 vue3 有细微差别// vue2// router.addRoutes(userRoute)// vue3// 因为 vue3 移除了 router.addRoutes()方法,所以需要手动实现addRoutes方法。addRoutes(userRoute);return next({ ...to });}// 有name说明路由存在,否则说明没有该路由if (to.name) {next();} else {// 去无权限页面next("/nopermission");}} else {// 否则去登录页next("/login");}} else {// 是白名单则直接进入next();}
});// 修改标题的工作可以放在全局后置守卫
router.afterEach((to, from) => {if (to.meta.title) {document.title = to.meta.title;}
});
我们再来看看 store 的逻辑
js
复制代码
import { createStore } from "vuex";export default createStore({state: {userMenus: null,},getters: {getUserMenus: (state) => state.userMenus,},mutations: {setUserMenus(state, payload) {state.userMenus = payload;},},actions: {async getUserMenuAction({ commit }) {// 模拟后端获取用户菜单信息apiconst getUserMenuApi = () => {// 假设只有about菜单return Promise.resolve([{path: "/about",name: "About",component: "../views/About.vue",title: "关于",},]);};const userMenus = await getUserMenuApi();commit("setUserMenus", userMenus);return userMenus;},},
});
使用
创建完路由后需要在 main.js 导入使用
js
复制代码
import router from "./router";// vue2写法
new Vue({router,render: (h) => h(App),
}).$mount("#app");// vue3写法
const app = createApp(App);
app.use(router).mount("#app");
在没登录的情况下,去about和userlist页面会重定向到登录页。
给本地添加token,模拟登录完成后,因为用户有about的菜单,所以去 about 页面是没问题的,去 userlist 页面则会重定向到没有权限页面。
总结
优点:
系统角色可以不固定,支持动态添加角色。适用于后台大型管理系统。
缺点:
实现相对复杂,而且需要后端配合的更多。
总结
第一种通过 beforeEach 实时监测用户权限的方式优势是实现简单,路由的权限在 meta 的 roles 里面配置。用户信息里面只需要包含当前用户的角色。缺点呢也很明显,系统所拥有的角色必须事先知道,然后以死代码的形式配置在 routes 里面,不支持动态添加角色。笔者觉得如果是做角色固定的系统是非常好的,比如什么图书管理系统、教师管理系统等。
第二种通过 通过 addRoutes 动态添加路由实现角色权限的方式优势是系统角色可以不固定,支持动态添加角色。很适用于后台大型管理系统,可以实时创建角色分配菜单。
当然、缺点就是实现起来比较复杂,需要后端一起参与设计。
相关文章:
企业级Vue路由角色权限应该怎么做?
角色权限 角色权限,简单来说就是登录的用户能看到系统的哪些页面,不能看到系统的哪些页面。一般是后台管理系统才会涉及到如此复杂的角色权限。 对于 vue 技术栈,实现角色权限一般有两种方式。 第一种是利用 beforeEach 全局前置守卫。 第…...
3.2.0 版本预告!Apache DolphinScheduler API 增强相关功能
Apache DolphinScheduler 3.2.0 版本即将发布,在此之前,为了让用户提前了解到大家所期待的新功能,我们制作了视频来”剧透“一些核心新发布。此前,我们比较全面地”剧透“的 3.2.0 版本的新功能,这次,我们来…...
测试工程师的工作
目录 1.何为软件测试工程师? 2.软件测试工程师的职责? 3.为什么要做软件测试? 4.软件测试的前途如何? 5.工具和思维谁更重要? 6.测试和开发相差大吗? 7.成为测试工程师的必备条件 8.测试的分类有哪…...
压力测试与测试工具jmeter的介绍
目录 一、性能指标 二、jmeter (一)JMeter 安装 (二)JMeter 压测示例 1、添加线程组 2、添加 HTTP 请求 3、添加监听器 4、启动压测&查看分析结果 (三)JMeter Address Already in use 错误解决 压力测…...
解析整型最大值(Integer.MIN_VALUE)溢出变为最小值(Integer.MAX_VALUE)
解析整型最大值(Integer.MIN_VALUE)溢出变为最小值(Integer.MAX_VALUE)结论分析 解析整型最大值(Integer.MIN_VALUE)溢出变为最小值(Integer.MAX_VALUE) 解析整型最大值(Integer.MIN_VALUE)溢出变为最小值(Integer.MAX_VALUE) ,java 二进制 最小值 减法 减1 结论 …...
【openpcdet】dbinfo内的信息
这就是kitti_dbinfos_train_sfd_seguv.pkl中【car】类别存储的信息。...
clickhouse查询缓存
为了实现最佳性能,数据库需要优化其内部数据存储和处理管道的每一步。但是数据库执行的最好的工作是根本没有完成的工作!缓存是一种特别流行的技术,它通过存储早期计算的结果或远程数据来避免不必要的工作,而访问这些数据的成本往…...
vue中使用Base64加密、解密以及des加密、解密
Base64加密、解密 第一步: npm install js-base64 --save 下载依赖 第二步: 直接引入即可 import { Base64 } from js-base64; 第三步: Base64.encode(xxxx) 其中 .encode() 加密 .decode() 解密 中间不需要使用加密的key等…...
关于丢失安卓秘钥的撞sha-1值的办法
实验得知,安卓sha-1和keytool生成秘钥签名文件的时间有关。 前提条件是,开发者必须知道生成秘钥的所有细节参数 以下是撞文件代码(重复生成) import time import osidx 0while True:cmdkeytool -keyalg RSA -genkeypair -alia…...
maven如何打包你会吗?
1.新建一个maven项目,在main/java中建立Main类 public class Main {public static void main(String[] args) {System.out.println("hello java ...");} } 2.添加依赖,使其成为可执行包 <build><plugins><!--打包成为可执行包-…...
idea 控制台 打印 Tomcat日志Tomcat Catalina Log控制台乱码问题
修改tomcat的日志配置文件 conf一>logging.properties 修改【1catalina.org.apache.juli.AsyncFileHandler.encoding】的值为gbk 1catalina.org.apache.juli.AsyncFileHandler.level FINE 1catalina.org.apache.juli.AsyncFileHandler.directory ${catalina.base}/logs 1…...
python我的世界
我的世界不知道大家有没有玩过,今天博主用python的Ursina库复刻了我的世界给大家分享 安装Ursina pip install ursina 导入Ursina from ursina import * from ursina.prefabs.first_person_controller import FirstPersonController 创建app app Ursina() 创建Voxe…...
SpringBoot+vue 大文件分片下载
学习链接 SpringBootvue文件上传&下载&预览&大文件分片上传&文件上传进度 Blob & File & FileReader & ArrayBuffer VueSpringBoot实现文件的分片下载 video标签学习 & xgplayer视频播放器分段播放mp4(Range请求交互过程可以参…...
scanf函数读取数据 清空缓冲区
scanf函数读取数据&清空缓冲区 scanf 从输入缓冲区读取数据数据的接收数据存入缓冲区scanf 中%d读取数据scanf中%c读取数据 清空输入缓冲区例子用getchar()吸收回车练习 scanf 从输入缓冲区读取数据 首先,要清楚的是,scanf在读取数据的时候ÿ…...
js 文件常用转换
获取上传文件的arrayBuffer:var u8arr await file.arrayBuffer() 通过arrayBuffer转换成Buffer:Buffer.from(u8arr) 1. Blob、File → Base64 function fileToDataURL(file) {let reader new FileReader();reader.readAsDataURL(file);reader.onload…...
基于Open3D的点云处理15-特征点
Intrinsic shape signatures (ISS) 参考 ISS关键点: 基本原理是避免在沿主要方向表现出类似分布的点上检测关键点,在这些点上无法建立可重复的规范参考框架,因此后续描述阶段很难变得有效。在剩余点中,显着性由最小特征值的大小决定,以便仅包…...
算法刷题Day 58 每日温度+下一个更大元素I
Day 58 单调栈 739. 每日温度 class Solution { public:vector<int> dailyTemperatures(vector<int>& temperatures) {vector<int> rst(temperatures.size());vector<int> decsStk; // 单调递减栈for (int i 0; i < temperatures.size(); i)…...
认识 spring AOP (面向切面编程) - springboot
前言 本篇介绍什么是spring AOP, AOP的优点,使用场景,spring AOP的组成,简单实现AOP 并 了解它的通知;如有错误,请在评论区指正,让我们一起交流,共同进步! 文章目录 前言1. 什么是s…...
将css文件中的px转化为rem
pxToRem.js /*** 使用方式:* 与引入的文件放在同一目录下进行引用配置,执行:node (定义的文件)*/ const fs require(fs) const path require(path) /*** entry:入口文件路径 type:Strng* pxtopx:以倍数转…...
JNI之Java实现远程打印
打印机是最常见的办公设备了。一般情况下如果需要实现打印,可通过前端print.js包来完成。但是,如果要实现智能办公打印,就可以使用JNI技术、封装接口、远程调用实现完成。 导包 jacob:Java COM Bridge <dependency><g…...
PDFMathTranslate:突破语言障碍的学术文档翻译终极解决方案
PDFMathTranslate:突破语言障碍的学术文档翻译终极解决方案 【免费下载链接】PDFMathTranslate PDF scientific paper translation with preserved formats - 基于 AI 完整保留排版的 PDF 文档全文双语翻译,支持 Google/DeepL/Ollama/OpenAI 等服务&…...
Vmware系列虚拟机系列【仅供参考】:解决 VMware 嵌套虚拟化提示 关闭“侧通道缓解“
解决 VMware 嵌套虚拟化提示 关闭“侧通道缓解“ 解决 VMware 嵌套虚拟化提示 关闭"侧通道缓解" 解决方法 方法1: 方法2: 完全禁用 Hyper-V 方法3 参考链接: 解决 VMware 嵌套虚拟化提示 关闭"侧通道缓解" 最近给电脑做了新版的 Windows 11 LTSC操作系…...
、SEATA分布式事务——XA模式
指令替换 项目需求:将加法指令替换为减法 项目目录如下 /MyProject ├── CMakeLists.txt # CMake 配置文件 ├── build/ #构建目录 │ └── test.c #测试编译代码 └── mypass2.cpp # pass 项目代码 一,测试代码示例 test.c // test.c #includ…...
用ESP32-S3和百度AI做个会聊天的智能音箱(Arduino+文心一言+语音识别)
用ESP32-S3和百度AI打造会聊天的智能音箱:从硬件组装到语音交互全流程 想象一下,清晨醒来只需对桌上的小盒子说句"今天天气如何",就能听到温柔的女声播报天气预报;工作时随口问"量子计算是什么",立…...
别再乱装CUDA了!保姆级教程:从显卡驱动到PyTorch 2.x,一次搞定Windows深度学习环境
深度学习环境配置避坑指南:从显卡驱动到PyTorch 2.x全流程解析 刚接触深度学习的开发者,往往在环境配置阶段就遭遇重重阻碍。显卡驱动与CUDA版本不匹配、cuDNN安装失败、PyTorch下载缓慢等问题,让许多初学者在起步阶段就耗费大量时间。本文将…...
Cogito 3B实战案例:GitHub PR描述自动生成+变更点总结
Cogito 3B实战案例:GitHub PR描述自动生成变更点总结 1. 快速了解Cogito 3B模型 Cogito v1预览版是Deep Cogito推出的混合推理模型系列,这个3B版本在大多数标准基准测试中都表现出色,超越了同等规模的其他开源模型。简单来说,它…...
换掉 Notepad++,事实证明它更牛逼!
提到文本编辑工具,大家肯定第一时间想到的是 Notepad 。Notepad 是一种流行的源代码编辑器,也是 Windows 用户的可靠记事本替代品。它是一个功能强大的实用程序,可在不占用大量存储空间的情况下提供最佳性能。不幸的是,它不适用于…...
从零开始:roLabelImg安装与OBB旋转框标注实战指南
1. 为什么需要roLabelImg和旋转框标注 在计算机视觉项目中,我们经常需要标注图像中的目标物体。对于常规的矩形框标注,LabelImg这类工具已经足够好用。但遇到倾斜物体时,比如遥感图像中的飞机、自然场景中的交通标志、医学图像中的器官&#…...
Android项目中的Gradle文件详解:从基础配置到高级技巧
Android项目中的Gradle文件详解:从基础配置到高级技巧 在Android开发的世界里,Gradle文件就像是一个项目的"大脑",它控制着构建过程的方方面面。对于有一定经验的Android开发者来说,深入理解Gradle文件的配置不仅能够提…...
jsDelivr CDN:如何为你的开源项目选择最佳加速方案
1. 为什么你的开源项目需要jsDelivr CDN 作为一个开源项目维护者,我深刻理解静态资源加载速度对用户体验的影响。去年我的一个Vue组件库项目就遇到过这样的问题:海外用户访问飞快,但国内用户总是抱怨加载缓慢。直到我把资源托管到jsDelivr&am…...
