本文已获得作者授权转载作者:谦虚前端原创:https://juejin.im/post/689229...前言本人主要负责中和开发在公司的后台系统中,包括路由和权限的调校是非常重要也是最基础的部分。在实际开发项目中,登录和路由权限的控制参考了明星项目vue-element-admin,并在此基础上,基于业务进行集成。接下来我就以这个项目为例,仔细分析一下整个路由和权限验证的过程,也算是对这个知识点的一些总结。项目整体目录结构在进入今天的主题之前,我们先梳理一下整个项目,在src目录下。api:接口请求资产:静态资源组件:通用组件指令:自定义指令过滤器:自定义过滤器图标:图标布局:布局组件(页面架构核心)路由器:路由配置(路由权限核心模块)商店:状态管理样式:style文件utils:工具方法views:页面组件permission.js权限管理对本项目感兴趣的同学可以自行学习,有针对性。除了路由权限验证的功能,它还包含了很多有趣的功能。相信能学到很多东西。路由权限控制逻辑路由处理流程图Routing2.png路由处理源码分析我们首先找到permission.js文件,里面定义了全局路由守卫,这也是路由权限中非常关键的核心代码。为了方便大家,只提取了路由相关的代码/nprogress.css'//进度条styleimport{getToken}from'@/utils/auth'//从cookie获取令牌NProgress.configure({showSpinner:false})//NProgressConfigurationconstwhiteList=['/login','/auth-redirect']//白名单配置router.beforeEach(async(to,from,next)=>{//启动进度条NProgress.start()//havetokenif(hasToken){if(to.path==='/login')//如果当前路径是/login,则重定向到首页next({path:'/'})NProgress.done()//hack:https://github.com/PanJiaChen/vue-element-admin/pull/2939}else{//判断用户是否通过getInfo获得了他的权限角色consthasRoles=store.getters.roles&&store.getters.roles.length>0if(hasRoles){next()}else{try{//获取用户信息const{roles}=awaitstore.dispatch('user/getInfo')//根据用户的角色,动态生成路由constaccessRoutes=awaitstore.dispatch('permission/generateRoutes',roles)//动态添加路由(将基本路由信息与动态路由合并)router.addRoutes(accessRoutes)//继续访问next({...to,replace:true})}catch(error){//删除令牌awaitstore.dispatch('user/resetToken')Message.error(error||'HasError')//重定向到登录页面next(`/login?redirect=${to.path}`)NProgress.done()}}}}else{//没有令牌if(whiteList.indexOf(to.path)!==-1){//如果在白名单中,不需要做任何校验,直接释放next()。}else{//如果不在白名单中,则重定向到登录页面。next(`/login?redirect=${to.path}`)NProgress.done()}}})router.afterEach(()=>{//完成进度条NProgress.done()})注意/login?redirect=\${jto.path},这里的redirect参数主要用于用户登录成功后重定向页面路径具体函数在/views/login/index.vue文件中watch:{$route:{handler:function(route){constquery=route.queryif(query){//路由查询参数this.redirect=query.redirectthis.otherQuery=this.getOtherQuery(query)}},immediate:true}},//undermethods:handleLogin(){//登录函数this.$refs.loginForm.validate(valid=>{if(valid){//账号密码验证成功后this.$store.dispatch('user/login',this.loginForm).then(()=>{//直接跳转到this.redirect路径的页面this.$router.push({path:this.redirect||'/',query:this.otherQuery})this.loading=false})}else{//..}})},动态路由配置来看看在routingfirst的定义,在/src/router/index.js文件中exportconstconstantRoutes=[//用于定义普通路由配置,//不需要访问权限的路由配置对象]exportconstasyncRoutes=[//通过路由元素在formationmeta.roles设置访问权限,一般是一个数组,meta:{title:'Permission',icon:'lock',roles:['admin','editor']//通过角色设置路由的权限},//...}]动态添加路由时,本质上是根据用户的角色信息过滤asyncRoutes路由配置数组中的路由,找到对应的路由,并与constantRoutes合并生成最新的路由动态路由逻辑图动态路由generation.png动态路由源码分析代码入口:permission.jsconstaccessRoutes=awaitstore.dispatch('permission/generateRoutes',roles)permission/generateRoutes方法入口文件:/strore/modules/permissions。jsimport{asyncRoutes,constantRoutes}来自'@/router'conststate={routes:[],addRoutes:[]}constmutations={SET_ROUTES:(state,routes)=>{state.addRoutes=routesstate.routes=constantRoutes.concat(routes)}}constactions={generateRoutes({commit},roles){returnnewPromise(resolve=>{letaccessedRoutesif(roles.includes('admin')){//如果包含admin,则解释是admin,可以访问所有模块accessedRoutes=asyncRoutes||[]}else{//如果不是管理员,需要根据用户角色roles和异步路由进行过滤accessedRoutes=filterAsyncRoutes(asyncRoutes,roles)}//将final结果存入vuexcommit('SET_ROUTES',accessedRoutes)//解析出resolve(accessedRoutes)})}}过滤异步路由,将最终结果存入vuex,将结果解析出exportfunctionhasPermission(roles,route){if(route.meta&&route.meta.roles){//如果existsmeta.roles//只要meta.roles中存在与用户角色列表中相同的值,则表示它具有访问权限returnroles.some(role=>route.meta.roles.includes(role))}else{//如果没有meta或者没有meta.roles,说明是通用模块,可以直接发布returntrue}}exportfunctionfilterAsyncRoutes(routes,roles){constres=[]routes.forEach(route=>{consttmp={...route}if(hasPermission(roles,tmp)){//判断路由数组中每一项的访问权限if(tmp.children){//如果有children,则递归调用filter函数tmp.children=filterAsyncRoutes(tmp.children,roles)}//将处理后的路由配置放入resres.push(tmp)}})returnres}最后返回/permission.js文件constaccessRoutes=awaitstore.dispatch('permission/generateRoutes',roles)//这里的accessRoutes是过滤后的路由,//最后合并constRoutes和accessRoutes通过route.addRoutes生成最终的访问路由router.addRoutes(accessRoutes)扩展按钮权限路由权限控制的基本过程已经分析过了。接下来我们看一下项目中按钮权限控制的实现。实现比较简单基本用法
