当前位置: 首页 > news >正文

企业级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

然后创建路由,vue2vue3创建路由的时候稍有区别,但是在路由鉴权那块是通用的。

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");

在没登录的情况下,去aboutuserlist页面都会重定向到登录页。

给本地添加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

然后创建路由,vue2vue3创建路由的时候稍有区别。

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");

在没登录的情况下,去aboutuserlist页面会重定向到登录页。

给本地添加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项目&#xff0c;在main/java中建立Main类 public class Main {public static void main(String[] args) {System.out.println("hello java ...");} } 2.添加依赖&#xff0c;使其成为可执行包 <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我的世界

我的世界不知道大家有没有玩过&#xff0c;今天博主用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&#xff08;Range请求交互过程可以参…...

scanf函数读取数据 清空缓冲区

scanf函数读取数据&清空缓冲区 scanf 从输入缓冲区读取数据数据的接收数据存入缓冲区scanf 中%d读取数据scanf中%c读取数据 清空输入缓冲区例子用getchar()吸收回车练习 scanf 从输入缓冲区读取数据 首先&#xff0c;要清楚的是&#xff0c;scanf在读取数据的时候&#xff…...

js 文件常用转换

获取上传文件的arrayBuffer&#xff1a;var u8arr await file.arrayBuffer() 通过arrayBuffer转换成Buffer&#xff1a;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关键点: 基本原理是避免在沿主要方向表现出类似分布的点上检测关键点&#xff0c;在这些点上无法建立可重复的规范参考框架&#xff0c;因此后续描述阶段很难变得有效。在剩余点中&#xff0c;显着性由最小特征值的大小决定,以便仅包…...

算法刷题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的优点&#xff0c;使用场景&#xff0c;spring AOP的组成&#xff0c;简单实现AOP 并 了解它的通知&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1. 什么是s…...

将css文件中的px转化为rem

pxToRem.js /*** 使用方式&#xff1a;* 与引入的文件放在同一目录下进行引用配置&#xff0c;执行:node &#xff08;定义的文件&#xff09;*/ const fs require(fs) const path require(path) /*** entry&#xff1a;入口文件路径 type:Strng* pxtopx&#xff1a;以倍数转…...

JNI之Java实现远程打印

打印机是最常见的办公设备了。一般情况下如果需要实现打印&#xff0c;可通过前端print.js包来完成。但是&#xff0c;如果要实现智能办公打印&#xff0c;就可以使用JNI技术、封装接口、远程调用实现完成。 导包 jacob&#xff1a;Java COM Bridge <dependency><g…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...