首页IT科技vue 权限管理(vue3 一个基于pinia简单易懂的系统权限管理实现方案,vue-router动态路由异步问题解决)

vue 权限管理(vue3 一个基于pinia简单易懂的系统权限管理实现方案,vue-router动态路由异步问题解决)

时间2025-04-29 12:59:33分类IT科技浏览4425
导读:前情提要 作为项目经验稀少的vue开发者来说,在关键技术点上的经验不多,我希望通过我的思想和实践,把好的东西分享在这里,目的是进一步促进技术交流。项目即将完成,权限是最后的收尾工作,好的权限实现方案,可以让我们没有后顾之忧,也可以提升项目的运行速度。...

前情提要

作为项目经验稀少的vue开发者来说             ,在关键技术点上的经验不多                   ,我希望通过我的思想和实践      ,把好的东西分享在这里      ,目的是进一步促进技术交流            。项目即将完成                   ,权限是最后的收尾工作            ,好的权限实现方案      ,可以让我们没有后顾之忧                   ,也可以提升项目的运行速度                   。

应用场景

在开发之前            ,我粗略的浏览了一些权限实现方法,可以说智者见智吧                   ,例如一种实现方案是在router的守卫里判断                  ,我认为虽然实现了功能,但是增加了路由的功能压力       。我们的需求是登录后即获知权限             ,根据权限提供功能;根据以上俩点需求我做出了如下计划:

找一个合适的入口                  ,判断要提供的菜单以及用户拥有什么权限      ,不能影响页面加载 在对应的页面中拿到权限             ,提供需要的功能 必须整个系统都可以轻易拿到                   ,且要避免安全问题

实战解析

大多的实现方案是添加路由      ,我也实践了一次      ,发现存在路由重定向问题                   ,于是我准备反其道而行            ,初始提供所有路由      ,根据权限移除不需要的路由                   ,上面我分析到            ,我不想给导航守卫添加过多的压力,避免影响页面渲染                   ,所以我利用了pinia状态库+router.removeRoute来实现路由的控制;

1             、控制添加路由

首先我们应该准备基础的router配置                  ,包括公共页面,异常页面等:

//router/index.js import { createRouter, createWebHistory } from vue-router import { useUsersStore } from "@/store/user"; import Cookies from "js-cookie"; import { isUserTime } from "@/tool/unitl.js" const routes = [ { path: /, name: Index, component: () => import(@/view/Index.vue), }, { path: /login, name: Login, component: () => import(@/view/Login.vue) }, { path: /business, name: Business, component: () => import(@/view/business/Index.vue) }, { path: /business/project, name: BusinessProject, component: () => import(@/view/business/Project.vue) }, { path: /commerce, name: Commerce, component: () => import(@/view/commerce/Index.vue) }, { path: /commerce/project, name: CommerceProject, component: () => import(@/view/commerce/Project.vue) }, { path: /consult, name: Consult, component: () => import(@/view/consult/Index.vue) }, { path: /consult/project, name: ConsultProject, component: () => import(@/view/consult/Project.vue) }, { path: /finance, name: Finance, component: () => import(@/view/finance/Index.vue), children: [ { path: projects/project, name: FinanceProjectsProject, component: () => import(@/view/finance/projects/Project.vue) }, { path: employee/project, name: FinanceEmployeeProject, component: () => import(@/view/finance/employee/Project.vue) }, { path: account, name: FinanceAccount, component: () => import(@/view/finance/account/Index.vue) }, { path: authority/project, name: FinanceAuthority, component: () => import(@/view/finance/authority/Project.vue) } ] }, { path: /chief, name: Chief, component: () => import(@/view/chief/Index.vue), children: [ { path: first_examine/project, name: firstExamineProject, component: () => import(@/view/chief/first_examine/Project.vue) }, { path: second_examine/project, name: secondExamineProject, component: () => import(@/view/chief/second_examine/Project.vue) } ] }, { path: /email, name: Email, component: () => import(@/view/email/Index.vue) }, { path: /person, name: Person, component: () => import(@/view/person/Detail.vue) }, { path: /:pathMatch(.*)*, name: 404, component: () => import(@C/tool/Page404.vue) } ] const router = createRouter({ history: createWebHistory(), //history routes }) router.beforeEach(async (to, from) => { //pinia仓库可以最早出现的地方             ,路由初始化完毕 const store = useUsersStore(); if (to.name == 404 && from.name == Login) { //处理404问题                  ,并且从login跳转任何404页面重置为Login页面 return { name: Login }; } else if (store.isLogin) { //不是404      ,登录状态在             ,正常跳转 return true; } else if (isUserTime() && Cookies.get("loginPwd")) { //刷新进入                   ,判断是否过期了登录时间      ,Cookie中是否存在密码 //如果存在免登录      ,如果登录过程存在路由不存在的问题则404                   ,否则正常跳转 let exist = await store.Login(to); if (!exist) return { name: 404 }; } else return { name: "Login" }; }); export default router

以上代码为router所有前期需要准备的代码            ,已经我处理路由异常情况的实现方案      ,核心是利用:

async await 俩个关键字实现                   ,因为在登录后            ,我们配置移除菜单和权限是需要时间的,必须要严格控制异步                   ,这里也顺便处理了路由输入回车跳转情况                  , 通过await store.Login(to);传入了to的参数来判断如何跳转,接下来参考下我的pinia仓库实现过程; //src/store/user.ts import { defineStore } from pinia import { apiGetUser } from @/axios/user.js import CryptoJS from crypto-js import router from @/router/index.js // 第一个参数是应用程序中 store 的唯一 id export const useUsersStore = defineStore(users, { state: () => { return { isLogin: false, phone: sessionStorage.UserN ? CryptoJS.AES.decrypt( sessionStorage.getItem(UserN), "abc!" ).toString(CryptoJS.enc.Utf8) : , pwd: "", deptno: "", idCard: "", lockState: "", birthday: "", name: "", sex: "男", power: ,//权限:0-admin,1-部门总负责人,2-部门项目负责人,3-部门成员 showMenu: , } }, actions: { Login(to) { let _that = this; let MenuArray = [Business, BusinessProject, Commerce, CommerceProject, Consult, ConsultProject, Finance, Chief]; return new Promise((t, f) => { _that.isLogin = true; let data = new FormData(); data.append(loginAct, _that.phone) apiGetUser(data).then(res => { let { deptno, idCard, lockState, name, sex, birthday } = res, showMenu = deptno.charAt(0); console.log(res); _that.$patch({ deptno, idCard, lockState, name, sex, birthday, showMenu }) if (showMenu == R) { t(true) } else { //过滤菜单项 switch (showMenu) { case A: MenuArray = MenuArray.filter(item => { return item.indexOf(B) == -1 }); break; case B: MenuArray = MenuArray.filter(item => { return item.indexOf(Com) == -1 }); break; case C: MenuArray = MenuArray.filter(item => { return item.indexOf(Con) == -1 }); break; case D: MenuArray = MenuArray.filter(item => { return item.indexOf(F) == -1 }); break; case E: MenuArray = MenuArray.filter(item => { return item.indexOf(Ch) == -1 }); break; } //循环移除 let Length = MenuArray.length; MenuArray.forEach((item, index) => { if (index == Length - 1) { router.removeRoute(item); if(to.name && router.hasRoute(to.name)){ t(true) }else{ t(false) } } else { router.removeRoute(item) } }) } }) }) }, }, })

这里我直接展示了我的代码             ,如果你不熟悉pinia,要抓紧补了                  ,在用户登录验证成功后      ,这里会调用user库的login函数             ,也会携带路由要to的参数                   ,这里只需要明确几个点即可:

MenuArray      ,将来用来循环removeRoute使用的数组      ,映射路由name 根据自己的配置规则                   ,筛选 MenuArray 通过index == Length - 1 严格控制最后一次移除            ,并且判断to.name是否合法 理解await原理      ,await必须出现在async函数中                   ,且 返回函数如果是Promise            ,则只接受resolved中的值; router, 熟悉vue3的同志知道 有一个useRouter 驱动函数,在我们的方案里不能那样使用                   ,因为useRouter必须在setup函数中使用;

2                   、实践观察

我们的需求完美实现                  ,接下来就是把不需要的菜单隐藏掉

3      、控制功能

路由我们已经控制住了,隐藏菜单就很容易了             ,加载userStore 获取条件v-if即可                  ,例如在header组件中:

//header.vue import { useUsersStore } from "@/store/user.js" const user = useUsersStore(); //html <el-menu-item index="/">首页</el-menu-item> <el-menu-item v-if="AR.indexOf(user.showMenu) != -1" index="/business">业务部</el-menu-item> <el-menu-item v-if="BR.indexOf(user.showMenu) != -1" index="/commerce">商务部</el-menu-item> <el-menu-item v-if="CR.indexOf(user.showMenu) != -1" index="/consult">咨询部</el-menu-item> <el-menu-item v-if="DR.indexOf(user.showMenu) != -1" index="/finance">财务部</el-menu-item> <el-menu-item v-if="ER.indexOf(user.showMenu) != -1" index="/chief">总工办</el-menu-item>

这边根据自己团队的权限规范条件处理即可      ,其他的控制举一反三

4      、解决异步路由问题

router.beforeEach(async (to, from) => { //pinia仓库可以最早出现的地方             ,路由初始化完毕 const store = useUsersStore(); if (to.name == 404 && from.name == Login) { //处理404问题                   ,并且从login跳转任何404页面重置为Login页面 return { name: Login }; } else if (store.isLogin) { //不是404      ,登录状态在      ,正常跳转 return true; } else if (isUserTime() && Cookies.get("loginPwd")) { //刷新进入                   ,判断是否过期了登录时间            ,Cookie中是否存在密码 //如果存在免登录      ,登录过程存在路由不存在问题则404                   ,否则正常跳转 let exist = await store.Login(to); if (!exist) return { name: 404 }; } else return { name: "Login" }; });

这里是我项目的路由守卫            ,它已经完成绝大部分功能,登录权限判断                   ,404处理                  ,刷新进入,状态持久化             ,异步操作在网络环境中是无法避免的                  ,router官方示例也明确提醒了我们如何使用      ,这里我们是利用服务器数据判断要移除的菜单             ,且不说http响应异步                   ,router.removeRoute()也只能是一次删除一个      ,所以在store.Login(to)我严格控制了逻辑      ,并且也解决了上述问题,使用我这样的全局守卫来配置必须要理清楚逻辑:依靠 async及await来实现                   ,缺点是会有短暂的延迟            ,还不够流畅      ,但是笔者由于时间问题                   ,不能再优化下去            ,希望大家吸取精华去掉糟粕            。

至此我的分享结束,如果大家有更好的解决方案                   ,希望可以进一步交流                  。

最后

📚 vue专栏

☃️ 个人简介:一个喜爱技术的人       。

🌞 励志格言: 脚踏实地                  ,虚心学习      。

❗如果文章还可以,记得用你可爱的小手点赞👍关注✅             ,我会在第一时间回                   、回访                  ,欢迎进一步交流                  。
声明:本站所有文章      ,如无特殊说明或标注             ,均为本站原创发布             。任何个人或组织                   ,在未征得本站同意时      ,禁止复制            、盗用      、采集                   、发布本站内容到任何网站            、书籍等各类媒体平台      。如若本站内容侵犯了原著者的合法权益      ,可联系我们进行处理                  。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
帝国采集软件(帝国CMS采集视频教程-轻松打造自己的全方位视频网站) 大学生如何支配生活费(大学生如何?钱-大学生“生活费”在什么水平,才比较合理?可对照这张档次表)