企业级Vue路由角色权限应该怎么做?

2023-11-10

角色权限

角色权限,简单来说就是登录的用户能看到系统的哪些页面,不能看到系统的哪些页面。一般是后台管理系统才会涉及到如此复杂的角色权限。

对于 vue 技术栈,实现角色权限一般有两种方式。

第一种是利用 beforeEach 全局前置守卫。

第二种是利用 addRoutes 方法。

我们先来看看第一种

通过 beforeEach 实时监测用户权限

这种方案的核心就是首先在 routes 里面事先定义好路由的权限,然后在 beforeEach 全局前置守卫里面进行权限逻辑判断。看用户所拥有的角色和我们配置在路由里面的 roles 是否相匹配。匹配则允许进入,不匹配则重定向到无权限提示页面。

定义 routes

首先我们定义好系统的路由,对于非首页,我们一般都会使用路由懒加载。

在 meta 里面可以定义我们需要的元数据,这里需要加上我们路由的角色roles。也就是说进入该路由用户所需要具备的角色权限。如果没定义则代表任意角色都能进入。

js
复制代码
// router/routes.js

import 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) {
    // 获取token
    const 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 }) {
      // 模拟后端获取用户信息的api
      const 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)) {
    // 获取token
    const token = localStorage.getItem("token");

    // 如果有token
    if (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 }) {
      // 模拟后端获取用户菜单信息api
      const 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 动态添加路由实现角色权限的方式优势是系统角色可以不固定,支持动态添加角色。很适用于后台大型管理系统,可以实时创建角色分配菜单。

当然、缺点就是实现起来比较复杂,需要后端一起参与设计。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

企业级Vue路由角色权限应该怎么做? 的相关文章

  • 为什么 lodash 将我的数组转换为对象?

    我是 lodash 的新手 创建了一个函数 该函数从值为 null 或空白的对象中删除键 但是当我传递包含某些部分作为数组的对象时 它会删除数组并将其转换为对象 下面是我尝试过的代码 mixin removeFalsies this rem
  • 开玩笑错误意外的令牌...(ES6)

    每当我在命令行中运行 jest 时 都会收到以下错误 Test suite failed to run Users
  • 元素存在之前的html5音频绑定时间更新

    我试图从音频标签绑定 timeupdate 事件 该标签尚不存在 我习惯这样做 body on click selector function e 我用音频标签尝试了这个 body on timeupdate audioPlayerJS a
  • 应该使用encodeURI吗?

    javascript 的encodeURI 函数有任何有效用途吗 据我所知 当您尝试发出 HTTP 请求时 您应该 完整的 URI 您想要放入 URI 中的某些片段 可以是 unicode 字符串或 UTF 8 字节序列 在第一种情况下 显
  • 在 d3 中应用转换时出现错误

    我正在尝试对我在 d3 中设计的条形图应用一些过渡效果 这是我的代码 svg selectAll bar data data enter append g attr class bar append rect attr rx barRadi
  • 如何使用 JavaScript 上传文件而不进行回发?

    我正在 ASP NET 中进行文件上传 我用了
  • jQuery 在附加元素后立即返回 div 元素的高度 0

    我有一个浮动 div 最初没有内容 我使用 jQuery 将一组元素附加到 div 然后立即调用原始 div 的 height 方法 我添加的元素在样式表中具有定义的最小高度 而浮动 div 则没有 问题是 当我在原始 div 上调用 he
  • Jquery:排除元素

    我有以下代码 document ready function a rel each function this qtip content text img class middle src i icon processing gif alt
  • 如何使用 JavaScript 压缩文件?

    有没有办法使用 JavaScript 来压缩文件 例如 在雅虎邮件中 当您选择下载电子邮件中的所有附件时 它会被压缩并下载到单个 zip 文件中 JavaScript 能够做到这一点吗 如果是这样 请提供一个编码示例 我发现这个图书馆叫js
  • 如何格式化折线图谷歌图表材料上的轴?

    我在格式化材料图表的轴时遇到问题 Using classic line chart if I would like to format my vertical axis with a dollar sign I would do vAxes
  • 如何将 vue3-openlayers 插件添加到 nuxt

    我有以下 main ts 文件Vue3 https v3 vuejs org import createApp from vue import App from App vue How to do this in nuxt3 import
  • 如何使用 javascript 将我的域名字母大写?

    假设我的域名是www hello com 如何使用 jQuery JavaScript 使浏览器的 URL 栏看起来像 www HELLO com 您无法更改浏览器地址栏中显示的内容 这是一项基本的安全功能 您可以使您的域名全部大写 并将页
  • React Native 中 fontAwesome 图标的圆形轮廓

    我想使用 fontAwesome 图标 使其位于圆圈的中间 我想将它用作一个图标项 我读到我们可以将它与圆形图标一起使用并将其放置在其中 但我无法使其工作 import IconFA from react native vector ico
  • axios 如何将 blob 与 arraybuffer 作为响应类型处理?

    我正在下载一个 zip 文件axios https www npmjs com package axios 为了进一步处理 我需要获取已下载的 原始 数据 据我所知 Javascript 有两种类型 Blob 和 Arraybuffers
  • 如何使用 ReactJS 使表中的列可以以两种方式排序

    我正在 ReactJS 中构建一个简单的应用程序 它通过调用某个 API 来使用 JSON 数组 然后我将数组的结果填充到表中 我现在想让表的列可排序 我理想的情况是同时进行升序和降序排序 一旦我单击标题 当它按升序排序时 它应该按降序排序
  • 鼠标输入时反应显示按钮

    我有一个反应组件 它包含如下方法 mouseEnter console log this is mouse enter render var album list const albums this props if albums user
  • 分配函数后如何删除 onmouseout 事件?

    我有一个问题 我正在为 onmouseout 事件分配一个函数 但运行该事件后 我需要将其删除 将非常感谢您的帮助 这取决于你的代码 如果你用 d3 这样做 那么你可以说 在 onmouseout 事件函数中 element on mous
  • 反转比例函数

    这对我来说很有趣 看下面的D3代码 var scale d3 scale linear domain 100 500 range 10 350 scale 100 Returns 10 scale 300 Returns 180 scale
  • javascript 加壳器与压缩器

    我想知道加壳器与压缩器的区别 优点是什么 即您应该在网络应用程序中部署压缩版本还是压缩版本 示例代码 var layout NAVVISIBLE 1 Init function this Resize Dimensions function
  • 如何使用node.js获取屏幕分辨率

    我需要使用 node js 获取屏幕分辨率 但以下代码不起作用 var w screen width var h screen height 这也行不通 var w window screen width var h window scre

随机推荐

  • Ubuntu和windows系统下安装odoo15 企业版终于安装成功了附带安装方法

    方法和社区版一样 用官方方法 轻松简单 界面清爽多了 比社区版 和14的企业版 比起来 效率高了很多 很多细节上的改变 网站模板多了 还有多了视频会议功能 安装方法 先安装数据库 sudo apt install postgresql y
  • Windows------openvino 2022.1安装步骤

    openvino安装 1 下载安装包 官网链接 https www intel com content www us en developer tools openvino toolkit download html 点击download
  • C#泛型方法的定义及使用

    在 C 语言中泛型方法是指通过泛型来约束方法中的参数类型 也可以理解为对数据类型设置了参数 如果没有泛型 每次方法中的参数类型都是固定的 不能随意更改 在使用泛型后 方法中的数据类型则有指定的泛型来约束 即可以根据提供的泛型来传递不同类型的
  • [疯狂Java]AWT:菜单栏、菜单、菜单项、菜单事件处理

    1 菜单栏 菜单 菜单项之间的关系 1 菜单栏 MenuBar 就是窗口中常见的顶层菜单栏 包含文件 编辑 格式等等子菜单的菜单条 即包含菜单的容器 2 菜单 Menu 是必定包含菜单项或者菜单 嵌套包含菜单 也叫子菜单 的容器 3 菜单项
  • python爬虫入门案例(爬取lol所有英雄名称及技能)

    滴滴滴 这几天忙着增强自己的实力 发现了一个非常适合新手的案例 案例就是爬取英雄联盟的所有英雄名称和技能 废话不多说 我们来分析分析 要练手的链接 进入此网站我们会发现 网页上的响应数据里面并没有我们想要的数据 由此我们可以猜测 该响应的数
  • onvif协议常见错误总结

    1 返回28 TCP ERROR或者error 28 SOAP ENV Receiver No route to h 可能存在的原因 原因可能是账号密码错误 或者设备不在线的原因 导致TCP连接超时 或者onvif的地址写错导致的 我当时是
  • 使用easy excel进行简单的excel表格导入导出

    1 创建项目 导入easy excel的依赖
  • 腾讯云DDoS攻击防护指南

    1 什么是DDoS攻击 DDoS是目前成本较低的一种攻击方式之一 攻击者通过控制大量肉鸡 被黑客入侵控制的终端 同时向目标站点发起访问 目标站点被大量涌入的访问会话占满性能 而无法接收实际正常用户的访问请求 形成 拒绝服务 攻击 2 腾讯云
  • 基于遗传算法二维下料问题/矩形件排样/matlab程序

    基于遗传算法的二维板材切割下料优化问题 matlab程序 关键词 遗传算法 二维板材切割 matlab 引言 二维板材切割问题在实际的工程中有很多的应用 该问题基本等同于矩形件优化排样 具体是指将若干尺寸不相同的矩形零件在给定的矩形板材上以
  • 打靶练习:DC-1

    文章目录 主机发现和nmap信息收集 web信息收集 漏洞扫描 密码爆破 获得系统立足点 MySQL渗透 web渗透 用户提权 总结 主机发现和nmap信息收集 主机发现 kali kali sudo arp scan l 查找到主机地址为
  • 计算机最最基础的原理

    这是一篇知乎的文章详细的介绍了电子计算机运算器加减的原理及存储的原理 作者 张大昭 链接 https www zhihu com question 20112194 answer 84394468 来源 知乎 著作权归作者所有 商业转载请联
  • Heroku登录失败

    Heoku 在国内 注册和登录是个大问题 不知道原来怎么注册上了 如今需要登录删除 app 就是删除不了 今天努力找了个vpn 无奈还是登录不成功 https id heroku com login 在群里问了下有人能上 就知道肯定设置的有
  • Android Update Engine 分析(二十)为什么差分包比全量包小,但升级时间却更长?

    本文为洛奇看世界 guyongqiangx 原创 转载请注明出处 原文链接 https blog csdn net guyongqiangx article details 132343017 0 导读 时不时有同学在 OTA 讨论群和 V
  • 华为OD机试真题-机房布局/栈解法【2023.Q1】

    小明正在规划一个大型数据中心机房 需要满足的条件是 确保在每个机柜边上至少要有一个电箱 已知 机房排成1排 我们用M表示机柜 I表示间隔 请你返回这整排机房 至少需要多少个电箱 如果无解请返回 1 输入描述 第一行输入一个字符串 由 M 和
  • SqliLabs Less21-22

    第二十一关 基于单引号和括号 64位编码的密码注入 1 判断注入点 有了20关的经验 输入爆破账户 admin密码 admin后抓包 发现admin输入为YWRtaW4 3D 根据提示用64位数据解码得到YWRtaW4 用url编码 为 3
  • 新老域名更替时的页面跳转

    不少站长都有换域名的经历 新买的www xxx com的域名和老域名都已经绑定在空间上了 但老域名总是不敢轻易拿掉 更换的话 跟老域名相关的所有链接都将失效 出现404的页面非常不友好 用户也将找不到原来的内容 其实用JS跳转功能可以帮站长
  • 机器学习零基础?手把手教你用TensorFlow搭建图像识别系统

    转 http www leiphone com news 201701 Y4uyEktkkwb5YhJM html http www leiphone com news 201701 2tH3DgLmsGhnDd8D html 导语 这是W
  • 基于nb-iot和arduino的气象站(一)

    基于nb iot和arduino的气象站 一 温湿度和紫外线传感器 上一篇已经介绍了项目的大概情况 这一篇先讲温湿度传感器和紫外线传感器的使用 一 温湿度传感器 我使用的温度传感器为DHT21 DHT21数字温湿度传感器是一款含有已校准数字
  • [Java基础系列第5弹]Java多线程:一篇让你轻松掌握并发编程的指南

    多线程是一种编程技术 它可以让一个程序同时执行多个任务 从而提高程序的性能和效率 但是 使用Java多线程也不是一件容易的事情 它涉及到很多复杂的概念和问题 如线程安全 同步 锁 原子类 并发集合 生产者消费者模式 线程池模式 Future
  • 企业级Vue路由角色权限应该怎么做?

    角色权限 角色权限 简单来说就是登录的用户能看到系统的哪些页面 不能看到系统的哪些页面 一般是后台管理系统才会涉及到如此复杂的角色权限 对于 vue 技术栈 实现角色权限一般有两种方式 第一种是利用 beforeEach 全局前置守卫 第二