当前位置: 首页 > Web前端 > HTML

如何在vue中实现后台管理系统的权限控制

时间:2023-04-02 22:19:17 HTML

原文首发于本人博客,欢迎点击查看以获得更好的阅读体验~1.前言广告机项目中,角色权限管理是一个由来已久的难题。首先,我们确定的权限控制分为两部分,按照粒度比较细:接口访问权限控制页面的权限控制菜单中的页面是否可以通过按钮访问page(add,delete,modify)是否显示权限控制我们来看看这些权限控制是如何实现的。2.接口访问权限控制接口权限是对用户的验证。通常情况下,当用户登录时,服务器需要返回一个Token给前台,然后前台每次调用接口都需要带上这个Token,然后服务器获取到这个Token并进行比较,如果它通过了,就可以访问了。现有的方法是在登录成功回调中将后台返回的Token直接存储在sessionStorage中,然后在请求时将Token取出放在headers中传递给后台。代码如下:this.$http({method:'get',url:'test/query?id=20',withCredentials:true,headers:{token:sessionStorage.getItem('token'),name:sessionStorage.getItem('name')//响应后台要求传入Username}}).then(response=>{//请求成功后的操作})后来在一些文章中发现axios可以直接塞Token进入拦截器中的config.headers.Authorization并将其作为全局传递。以下是代码部分://main.jsimportaxiosfrom'axios'//实例化axios并设置超时constservice=axios.create({timeout:5000})//baseURL//axios.defaults.baseURL='https://api.github.com';//http请求拦截器//每个请求都会在http头部添加一个Authorization字段,内容为tokenservice.interceptors.request.use(config=>{if(store.state.user.token){config.headers.Authorization=`token${store.state.user.token}`;}returnconfig},err=>{returnPromise.reject(err)});exportdefaultservice3.页面权限控制上面说过,页面权限控制分为两种:菜单中的页面是否可以访问;是否显示页面中按钮(增、删、改)的权限控制;这些权限一般都在固定的页面上进行配置保存并记录在数据库中。撇开按钮权限不谈,页面访问权限可以通过两种方式实现:显示所有菜单,当用户访问不在自己权限范围内的菜单时,提示权限不足,只显示权限范围内的菜单当前用户可以访问。如果用户通过url强制访问,会直接进入404,既然显示了就不能点击了,那是什么意思,你在逗我吗?所谓隐形不明显,综合考虑后,肯定是第二种选择更符合良好的用户体验。好了,我们现在来梳理一下页面访问的大概流程:梳理完流程,我们开始详细写。1.创建路由表创建路由表其实没有什么难度,按照vue-router官方文档给出的例子直接写就行了。但是由于有些页面不需要访问权限,所以需要将登录、404、维护等页面写到默认路由中,而将其他需要权限的页面写到变量或者文件中,可以有效减少后续维护压力。下面贴上index.js的代码,异步路由会适当减少,避免占用太多空间。//router/index.jsimportVuefrom'vue'importRouterfrom'vue-router'importAppfrom'@/App'importstorefrom'../store/index'Vue.use(Router);//手动跳转转发页面白名单constwhiteList=['/'];//默认不需要权限的页面constconstantRouterMap=[{path:'/',name:'login',component:(resolve)=>require(['@/components/login'],resolve)},{path:'/index',name:'nav.Home',component:(resolve)=>require(['@/components/index'],resolve)},{path:'/templateMake',name:'TemplateMake',component:(resolve)=>require(['@/components/Template/templateMake'],resolve)},{path:'/programMack',name:'程序制作',component:(resolve)=>require(['@/components/Template/programMack'],resolve)},{path:'/release',name:'程序发布',component:(resolve)=>require(['@/components/Program/release'],resolve)}]//注册路由exportconstrouter=newRouter({routes:constantRouterMap});//异步路由(需要权限的页面)导出常量asyncRouterMap=[{path:'/resource',name:'nav.Resource',meta:{permission:[]},component:(resolve)=>require(['@/components/Resource/resource'],resolve)},{path:'/template',name:'nav.Template',meta:{permission:[]},component:(resolve)=>require(['@/components/Template/template'],resolve)},{path:'/generalSet',name:'nav.System',meta:{permission:[]},component:(resolve)=>require(['@/components/SystemSet/generalSet'],resolve)},{path:'',name:'nav.Log',component:App,children:[{path:'/userLog',name:'nav.UserLog',meta:{permission:[]},component:(resolve)=>require(['@/components/Log/userLog'],resolve),},{path:'/operatingLog',name:'nav.SystemLog',meta:{permission:[]},component:(resolve)=>require(['@/components/Log/operatingLog'],resolve),},]}]];注意事项:这里要注意一点,404页面一定要最后加载。如果你和constantRouterMap一起声明404,那么后面的所有页面都会被404阻塞。具体问题请看addRouteswhenyou'vegotawildcardroutefor404sdoesnotwork2。页面访问权限一开始我们梳理了一个粗略的页面访问权限流程接下来我们先实现核心部分:我们先获取用户权限列表,这里我们就接触到vuex状态管理,官方文档有详细介绍,这里就不过多描述了,请看下面代码://存储/索引。jsimportAxiosfrom'axios'importVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex);constaxios=Axios.create();conststate={mode:'login',list:[]};constgetters={};constmutations={setMode:(state,data)=>{state.模式=数据},setList:(state,data)=>{state.列表=数据}};constactions={//获取权限列表getPermission({commit}){returnnewPromise((resolve,reject)=>{axios({url:'/privilege/queryPrivilege?id='+sessionStorage.getItem('privId'),methods:'get',headers:{token:sessionStorage.getItem('token'),name:sessionStorage.getItem('name')}}).then((res)=>{//存储权限列表提交('setList',res.data.cust.privileges[0].children);resolve(res.data.cust.privileges[0].children)}).catch(()=>{reject()})})}};exportdefaultnewVuex.Store({state,mutations,actions,getters})好了,我们现在请求后台获取权限数据,并将数据存储到vuex中。接下来,我们需要使用返回的数据来匹配之前编写的异步路由表,将匹配结果与静态路由表结合起来,形成最终的实际路由表。最重要的是使用了vue-router2.2.0版本新增的一个addRoutes方法。看看官方文档是怎么解释这个方法的:router.addRoutes(routes)2.2.0+动态添加更多的路由规则。参数必须是符合routes选项要求的数组。然后我们现在可以开始使用addRoutes进行路由匹配。看下面代码://router/index.js/***根据权限匹配路由*@param{array}permission权限列表(菜单列表)*@param{array}asyncRouter异步路由对象*/functionrouterMatch(permission,asyncRouter){returnnewPromise((resolve)=>{constrouters=[];//创建路由函数createRouter(permission){//添加根据路径匹配的路由对象到routerspermission.forEach((item)=>{if(item.children&&item.children.length){createRouter(item.children)}letpath=item.path;//循环异步路由,将满足权限列表的路由添加到路由器asyncRouter.find((s)=>{if(s.path===''){s.children.find((y)=>{if(y.path===path){y.meta.permission=item.permission;routers.push(s);}})}if(s.path===path){s.meta.permission=item.permission;routers.push(s);}})})}createRouter(permission)resolve([routers])})}然后我们编写导航挂钩//router/index.jsrouter.beforeEach((to,form,next)=>{if(sessionStorage.getItem('token')){if(to.path==='/'){router.replace('/index')}else{console.log(store.state.list.length);if(store.state.list.length===0){//如果没有权限列表,会向后台发起新的请求store.dispatch('getPermission').then(res=>{//调用权限匹配方法routerMatch(res,asyncRouterMap).then(res=>{//添加路由router.addRoutes(res[0]);next(to.path)})}).catch(()=>{router.replace('/')})}else{if(to.matched.length){next()}else{router.replace('/')}}}}else{if(whiteList.indexOf(to.path)>=0){next()}else{router.代替('/')}}});到这里我们就完成了页面访问的权限控制,接下来对操作按钮的权限部分进行说明4.还记得之前路由配置中多出来的代码吗?我们来看看://异步路由(需要权限的页面)exportconstasyncRouterMap=[{path:'/resource',name:'nav.Resource',meta:{permission:[]},component:(resolve)=>require(['@/components/Resource/resource'],resolve)},{path:'/template',name:'nav.Template',meta:{permission:[]},component:(解析)=>require(['@/components/Template/template'],resolve)},{path:'/generalSet',name:'nav.System',meta:{permission:[]},component:(解析)=>require(['@/components/SystemSet/generalSet'],resolve)},{path:'',name:'nav.Log',component:App,children:[{path:'/userLog',名称:'nav.UserLog',meta:{permission:[]},component:(resolve)=>require(['@/components/Log/userLog'],resolve),},{path:'/operatingLog',名称:'nav.SystemLog',遇见a:{permission:[]},component:(resolve)=>require(['@/components/Log/operatingLog'],resolve),},]}]];在routerMatch中为每个路由页面添加meta字段函数中,匹配到的详细权限字段赋值在这里。这样每个页面的路由对象中都会获取到这个字段。asyncRouter.find((s)=>{if(s.path===''){s.children.find((y)=>{if(y.path===path){//赋值y。meta.permission=item.permission;routers.push(s);}})}if(s.path===path){s.meta.permission=item.permission;routers.push(s);}})接下来我们写一个vue自定义指令判断页面中需要认证的元素,比如这样:/*3代表一个上传权限的ID,如果权限中有3个,就会显示按钮*/我们直接注册一个全局命令,使用vnode访问vue方法。代码如下://main.js//按钮权限命令Vue.directive('allow',{inserted:(el,binding,vnode)=>{letpermissionList=vnode.context.$route.meta.permission;if(!permissionList.includes(binding.value)){el.parentNode.removeChild(el)}}})至此,权限控制过程就彻底结束了,下面来看看完整的权限控制流程图结尾。5.路由控制完整流程图6.参考资料