当前位置: 首页 > Web前端 > vue.js

vue权限管理实现流程

时间:2023-04-01 00:27:52 vue.js

项目的完整代码可以在我的github中找到:https://github.com/luxiancan/...1.总体思路后端返回用户权限,而前端-end根据用户权限处理左侧菜单;all路由在前端定义,根据后端返回的用户权限过滤掉需要挂载的路由,然后使用addRoutes动态挂载路由。2.实现要点(1)路由定义,分为初始路由和动态路由。一般来说,初始路由只有login,其他路由挂载在home路由下,需要动态挂载。(2)用户登录,登录成功后获取token,保存到sessionStorage中,跳转到home。此时会进入路由拦截,根据token获取用户权限列表。(3)全局路由拦截,根据当前用户是否有token和权限列表做相应的判断和跳转,没有token时跳转到登录,有token但没有时发送请求获取权限权限列表等逻辑。(4)处理用户权限,在store.js中定义一个模块permission.js,专门用来处理用户权限相关的逻辑。用户权限列表和菜单列表存储在该模块中;(5)用户权限列表和菜单列表处理,前端路由必须有一个与后端返回权限的唯一标识(一般使用路由名称作为标识),并根据这个标识过滤出对应的路由.(6)左侧菜单要与用户信息和用户管理模块使用的菜单信息一致,统一使用store存储的变量。三、具体实现过程1、准备工作,路由定义/*router/index.js*//*初始路由*/letrouter=newRouter({mode:'history',routes:[{path:'/login',name:'login',component:()=>import('@/views/login.vue'),},]});/*router/index.js*//*动态添加的路由*/exportconstdynamicRoutes=[{path:'/',name:'home',component:()=>import('@/views/home.vue'),meta:{requiresAuth:true},children:[]},{路径:'/403',组件:()=>导入('@/views/error-page/403.vue')},{路径:'*',组件:()=>导入('@/views/error-page/404.vue')}];系统主要页面的路由,这些路由会经过权限过滤,添加到home路由的children中/*router/router.js*/exportdefault[//dashboard{path:'/dash-board',name:'dash-board',component:()=>import('@/views/dash-board.vue'),meta:{permitName:'dash-theboard'}},//...];2、用户登录用户进入登录页面,输入用户名、密码、验证码,点击登录,发送登录请求。登录成功后,将token保存在sessionStorage中,然后跳转到Homepage/home,进入路由拦截逻辑/*login.vue*///发送登录请求vm.$http.login(query).then(res=>{letdata=res.data;if(data.code===0){sessionStorage.token=data.data.token;//跳转到首页home,会触发全局路由拦截router.beforeEachvm。$router.push({name:'home'});}else{//警报消息}});3、全局路由拦截从打开本地服务http://localhost:2001开始,打开后会进入登录页面,那么判断的依据是什么?第一个是令牌。没有登录的用户获取不到token,登录的用户会把token保存到seesionStorage中,所以我们可以根据当前是否有token来判断是否登录。/*router/index.js*//*全局路由拦截*/router.beforeEach((to,from,next)=>{//根据是否有token判断是否登录if(!sessionStorage.token){//1.用户打开localhost时,to.matched===[],匹配的是一个空路由,需要重定向到login//2.重定向到login后,to.matched===[name:"login",path:"/login"...]就是上一步的登录页面//to.matched.some(item=>item.meta.requiresAuth)这句话的意思是输入的路由页面需要登录认证,negation是不需要登录,直接passif(to.matched.length>0&&!to.matched.some(item=>item.meta.requiresAuth)){next();//跳过并进入下一个导航挂钩。比如:在/login路由页面刷新页面就会出现这样的逻辑}else{next({path:'/login'});}}else{//现在有一个tokenif(!store.state.permission.permissionList){//如果没有permissionList,发送请求获取用户权限列表store.dispatch('permission/FETCH_PERMISSION').then(()=>{next({path:to.path,query:to.query});});}else{//现在有一个权限列表if(to.path!=='/login'){if(to.matched.length===0){//如果匹配的路由是https://172.24.1.117/?id=xxx&name=xxx,表示没有关联跳转权限,跳转到403next({path:'/403'});}else{下一个();}}else{//1.如果用户在地址栏手动输入/login,则重定向到上一个路由页面//next(from.fullPath);//2.如果用户在地址栏手动输入/login,清除token并刷新页面,会转到登录页面store.commit('goToLogin');}}}});(1)当用户打开localhost,此时没有token,匹配的是空路由,我们重定向到登录页面next({path:'/login'});(2)当用户在登录页面刷新页面时,也会进入路由拦截,此时匹配的是登录路由,而登录路由不需要登录验证(requiresAuth为空或false),所以直接跳过next()的执行;(3)用户在登录页面输入用户名和密码,登录成功并保存token,跳转到/home路由;(4)此时进入路由拦截,已经有了token,但是没有用户权限permissionList,然后发送请求获取用户权限列表,获取到权限后next({path:to.path,查询:to.query});继续往下;(5)再次进入路由拦截。此时有token和permissionList,可以根据实际业务跳转。上面代码是判断是否为当前登录路由,如果用户登录后,手动在地址栏输入/login,清除token,跳转到登录页面。其他逻辑与具体业务相关,不再赘述。4、处理用户权限处理用户权限,在store.js中定义一个模块permission.js,专门用来处理用户权限相关的逻辑。用户权限列表和菜单列表存储在该模块中;我们来看看permission.js。What:/*permission.js*//*由于权限中有很多逻辑,所以在vuex中增加了一个权限模块来处理权限相关的逻辑和变量*/importhttpRequestfrom'@/assets/js/service/HTTP';//http请求importhandleModulefrom'@/assets/js/common/handle-module';//处理路由和侧边栏的公共函数importrouter,{dynamicRoutes}from'@/router/index';//默认路由配置,动态路由配置importpermissionRouterfrom'@/router/router';//需要权限的路由配置//...exportdefault{namespaced:true,//这个模块封装度和复杂度都比较高易用性,所以单独做一个namespace//...actions:{//dispatch('permission/FETCH_PERMISSION')asyncFETCH_PERMISSION({commit,state}){//初始化路由表,注意这里一定要写,router.beforeEach拦截路由时,多次执行FETCH_PERMISSIONcommit('setPermission',[]);//发送请求获取后端返回的用户权限letdata=awaitgetUserByToken();让userPopedoms=data.userPopedoms||[];//保存用户的权限模块(去掉后台返回的login,不需要在侧边栏展示),用户管理模块可以使用,权限列表让userPopeList=userPopedoms.filter(v=>v.requestMapping!=='login');commit('setUserPopedoms',userPopeList);//根据权限过滤掉我们设置的路由,添加到path='/'children,就是home路由的childrenletroutes=handleModule.getRouter(userPopedoms,permissionRouter);让homeContainer=dynamicRoutes.find(v=>v.path==='/');//使用concat的目的是让分配用户的权限在children的第0项中homeContainer.children=routes.concat(homeContainer.children);//设置首页重定向,重定向到用户权限的第0项homeContainer.redirect=homeContainer.children[0]。姓名;//根据权限生成左侧导航菜单letsidebarMenu=handleModule.getSidebarMenu(userPopeList);commit('setMenu',sidebarMenu);//初始路由letinitialRoutes=router.options.routes;//动态添加路由只有刷新页面才会清除动态添加的路由信息??router.addRoutes(dynamicRoutes);//完成路由表commit('setPermission',[...initialRoutes,...dynamicRoutes]);}}};(1)首先让data=awaitgetUserByToken();发送请求获取用户权限,获取数据。data.userPopedoms的格式大致如下:[{'moduleGroupId':1001,'moduleGroupName':'dashboard','requestMapping':'dash-board'},{'moduleGroupId':1100,'moduleGroupName':'workbench','requestMapping':'work-bench','moduleList':[{'moduleId':1101,'moduleName':'to-doitems','requestMapping':'todo-list','moduleGroupId':1100},{'moduleId':1102,'moduleName':'MyFootprint','requestMapping':'my-footprint','moduleGroupId':1100}]},//...](2)然后,根据到我们写的路由数组,比较过滤得到我们想要的路由。路由格式在上面的“路由定义”中router/router.js中已经提到了。还需要根据用户权限对侧边栏菜单进行处理。为此,我们需要两个处理函数,一个是根据用户权限列表和路由数组进行过滤得到最终的路由,一个是根据用户权限处理得到侧边栏菜单。于是专门创建了另一个文件handle-module.js来存放这两个函数。/*handle-module.js*/consthandleModule={/***根据后台返回的权限和所有配置的路由,过滤出真正的路由*@param{Array}permissionList返回的用户权限列表后台*@param{Array}allRouter前端配置的所有动态路由的集合*@return{Array}过滤后的路由*/getRouter(permissionList=[],allRouter=[]){//权限的格式为[“部署管理”,“系统日志”]letpermissions=permissionList.reduce((acc,cur)=>{if(cur.moduleList&&cur.moduleList.length>0)cur=cur.moduleList;returnacc.concat(cur);},[]).map(v=>v.requestMapping);返回allRouter.filter(item=>permissions.includes(item.meta.permitName));},/***根据后台返回的权限,生成侧边栏*@param{Array}permissionList后台返回的用户权限列表*@return{Array}sidebarMenu生成的侧边栏数组*/getSidebarMenu(permissionList=[]){让sidebarMenu=[];permissionList.forEach(item=>{让menuItem={name:item.requestMapping,标题:item.moduleGroupName};menuItem.children=(item.moduleList||[]).map(child=>({name:child.requestMapping,title:child.moduleName}));sidebarMenu.push(menuItem);});返回侧边栏菜单;}};导出默认handleModule;(3)获取上面过滤的路由数组后,添加到path为'/'的children/*router/index.js*/{path:'/',name:'home',component:()=>import('@/views/home.vue'),meta:{requiresAuth:true,},children:[/*将上面获取的路由添加到这里*/]}(4)根据上面的权限生成侧边栏菜单后,保存在store中以备后用(5)在上面第三步添加home的children的动态路由后,就可以在路由中添加dynamicRoutes了。router.addRoutes(动态路由);(6)至此路由添加完成,即FETCH_PERMISSION操作完成,可以在action.then中调用next({path:to.path,query:to.query});进入路线,即进入家园。我们已经将home路由重定向到上面菜单的第一条路由信息,所以会进入系统菜单的第一页。刷新页面后,根据router.beforeEach的判断,如果有token但没有permissionList,会重新触发action发送请求获取用户权限,之前的逻辑又会走一遍,所以有没问题。注销后需要清除token并刷新页面。因为路由是通过addRoutes添加的,而vue-router没有删除路由的api,所以清除路由,清空store存储的各种信息,刷新页面是最稳妥的。相关文件目录截图:4.总结缺点:在全局路由守卫中,每次路由跳转都需要判断;每次刷新页面都需要重新发送请求获取用户权限;注销时,需要刷新一次页面,动态添加的路由和权限信息被清除;优点:菜单与路由分离,菜单的修改、添加、删除由后台控制,方便后期维护;使用addRoutes动态挂载路由可以控制用户在url中输入相关地址进行跳转;Vue权限管理还有其他实现方式,大家可以根据实际业务考虑进行调整。以上实现方式比较适合我们现有项目的需求。以上,大家有什么问题欢迎提问交流,喜欢就点个赞吧~

猜你喜欢