<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Vue路由權(quán)限控制分析

          共 6232字,需瀏覽 13分鐘

           ·

          2020-11-22 02:18

          本文已獲作者授權(quán)轉(zhuǎn)載

          作者:卑微前端?

          原文:https://juejin.im/post/6892299215871934472

          前言

          本人在公司主要負責(zé)中后臺系統(tǒng)的開發(fā),其中路由和權(quán)限校驗算是非常重要且最為基本的一環(huán)。實際開發(fā)項目中,關(guān)于登錄和路由權(quán)限的控制參照了vue-element-admin這個明星項目,并在此基礎(chǔ)上基于業(yè)務(wù)進行了整合,接下來我會以這個項目為例,仔細地剖析整個路由和權(quán)限校驗的過程,也算是對這個知識點的一些總結(jié)。

          項目總體目錄結(jié)構(gòu)

          進入今天主題之前,我們先來梳理下整個項目,src 目錄下的。

          • api: 接口請求
          • assets: 靜態(tài)資源
          • components: 通用組件
          • directive: 自定義指令
          • filters: 自定義過濾器
          • icons: 圖標(biāo)
          • layout: 布局組件(頁面架構(gòu)核心)
          • router: 路由配置(路由權(quán)限核心模塊)
          • store: 狀態(tài)管理
          • styles: 樣式文件
          • utils: 工具方法
          • views: 頁面組件
          • permission.js 權(quán)限管理

          對這項目感興趣的同學(xué)可以自行,有針對性地學(xué)習(xí),除了路由權(quán)限校驗的功能以外,也包含了很多有意思的功能,相信能夠?qū)W到不少東西。

          路由權(quán)限控制邏輯

          路由處理流程圖

          路由2.png

          路由處理源碼分析

          我們先找到 permission.js 文件,此處定義全局路由守衛(wèi),也是路由權(quán)限中非常關(guān)鍵的核心代碼。為方便大家閱讀,只摘取了跟路由相關(guān)的代碼

          import?router?from?'./router'
          import?store?from?'./store'
          import?{?Message?}?from?'element-ui'
          import?NProgress?from?'nprogress'?//?progress?bar
          import?'nprogress/nprogress.css'?//?progress?bar?style
          import?{?getToken?}?from?'@/utils/auth'?//?get?token?from?cookie
          ?NProgress.configure({?showSpinner:?false?})?//?NProgress?Configuration
          ?const?whiteList?=?['/login',?'/auth-redirect']?//?白名單配置
          ?router.beforeEach(async(to,?from,?next)?=>?{
          ?//?start?progress?bar
          ?NProgress.start()
          ?//?有token
          ?if?(hasToken)?{
          ?if?(to.path?===?'/login')
          ?//?如果當(dāng)前路徑為/login,重定向到首頁
          ?next({?path:?'/'?})
          ?NProgress.done()?//?hack:?https://github.com/PanJiaChen/vue-element-admin/pull/2939
          ?}?else?{
          ?//?determine?whether?the?user?has?obtained?his?permission?roles?through?getInfo
          ?const?hasRoles?=?store.getters.roles?&&?store.getters.roles.length?>?0
          ?if?(hasRoles)?{
          ?next()
          ?}?else?{
          ?try?{
          ?//?獲取用戶信息
          ?const?{?roles?}?=?await?store.dispatch('user/getInfo')
          ?//?根據(jù)用戶的角色,動態(tài)生成路由
          ?const?accessRoutes?=?await?store.dispatch('permission/generateRoutes',?roles)
          ?//?動態(tài)添加路由?(將基本的路由信息跟動態(tài)路由進行合并)
          ?router.addRoutes(accessRoutes)
          ?//?繼續(xù)訪問
          ?next({?...to,?replace:?true?})
          ?}?catch?(error)?{
          ?//?刪除token
          ?await?store.dispatch('user/resetToken')
          ?Message.error(error?||?'Has?Error')
          ?//?重定向到登錄頁面
          ?next(`/login?redirect=${to.path}`)
          ?NProgress.done()
          ?}
          ?}
          ?}
          ?}?else?{
          ?//?沒有token
          ?if?(whiteList.indexOf(to.path)?!==?-1)?{
          ?//?如果在白名單中,則不需要進行任何校驗,直接放行
          ?next()
          ?}?else?{
          ?//?如果不存在于白名單中,則重定向到登錄頁面.
          ?next(`/login?redirect=${to.path}`)
          ?NProgress.done()
          ?}
          ?}
          })
          ?router.afterEach(()?=>?{
          ?//?finish?progress?bar
          ?NProgress.done()
          })

          注意到,代碼中的/login?redirect=${jto.path}, 這里的 redirect 參數(shù)主要是用于,在用戶登錄成功后進行跳轉(zhuǎn)的頁面路徑。具體功能在/views/login/index.vue 文件下

          watch:?{
          ?$route:?{
          ?handler:?function(route)?{
          ?const?query?=?route.query
          ?if?(query)?{?//路由查詢參數(shù)
          ?this.redirect?=?query.redirect
          ?this.otherQuery?=?this.getOtherQuery(query)
          ?}
          ?},
          ?immediate:?true
          ?}
          },
          // methods下的:
          handleLogin()?{?//?登錄函數(shù)
          ?this.$refs.loginForm.validate(valid?=>?{
          ?if?(valid)?{?//?賬號密碼校驗成功后
          ?this.$store.dispatch('user/login',?this.loginForm)
          ?.then(()?=>?{
          ?//?直接跳轉(zhuǎn)到this.redirect?路徑的頁面
          ?this.$router.push({?path:?this.redirect?||?'/',?query:?this.otherQuery?})
          ?this.loading?=?false
          ?})
          ?}?else?{
          ?//?..
          ?}
          ?})
          },

          動態(tài)路由配置

          我們先來看看路由的定義,在/src/router/index.js 文件下

          export?const?constantRoutes?=?[?//?用來定義普通的路由配置,不需要訪問權(quán)限的
          ?//?路由配置對象
          ]
          export?const?asyncRoutes?=?[?//?通過路由元信息meta.roles來設(shè)置訪問權(quán)限,一般來說是個數(shù)組
          ?{
          ?path:?'/permission',
          ?component:?Layout,
          ?redirect:?'/permission/page',
          ?alwaysShow:?true,?//?will?always?show?the?root?menu
          ?name:?'Permission',
          ?meta:?{
          ?title:?'Permission',
          ?icon:?'lock',
          ?roles:?['admin',?'editor']?//?通過roles設(shè)置路由的權(quán)限
          ?},
          ?//?...
          ?}
          ]

          動態(tài)添加路由時,本質(zhì)上就是根據(jù)用戶的角色信息在 asyncRoutes 路由配置數(shù)組中進行路由篩選,找到相對應(yīng)的路由,與 constantRoutes 合并生成最新的路由。

          動態(tài)添加路由邏輯圖

          動態(tài)路由生成.png

          動態(tài)路由生成.png

          動態(tài)路由源碼分析

          代碼入口: permission.js

          const?accessRoutes?=?await?store.dispatch('permission/generateRoutes',?roles)

          permission/generateRoutes 方法入口文件:/strore/modules/permissions.js

          import?{?asyncRoutes,?constantRoutes?}?from?'@/router'
          const?state?=?{
          ?routes:?[],
          ?addRoutes:?[]
          }
          ?const?mutations?=?{
          ?SET_ROUTES:?(state,?routes)?=>?{
          ?state.addRoutes?=?routes
          ?state.routes?=?constantRoutes.concat(routes)
          ?}
          }
          ?const?actions?=?{
          ?generateRoutes({?commit?},?roles)?{
          ?return?new?Promise(resolve?=>?{
          ?let?accessedRoutes
          ?if?(roles.includes('admin'))?{
          ?//?如果包含了admin,則說明是admin,具有所有模塊的訪問權(quán)限
          ?accessedRoutes?=?asyncRoutes?||?[]
          ?}?else?{
          ?//?如果不是管理員,則需要根據(jù)用戶角色roles和異步路由進行篩選
          ?accessedRoutes?=?filterAsyncRoutes(asyncRoutes,?roles)
          ?}
          ?//?將最終的結(jié)果存放到vuex中
          ?commit('SET_ROUTES',?accessedRoutes)
          ?//?resolve出去
          ?resolve(accessedRoutes)
          ?})
          ?}
          }

          對異步路由進行篩選,并將最終的結(jié)果存放到 vuex 中,并將結(jié)果 resolve 出去

          export?function?hasPermission(roles,?route)?{
          ?if?(route.meta?&&?route.meta.roles)?{?//?如果存在meta.roles
          ?//?只要meta.roles中存在與用戶角色列表中相同的值,則說明具有訪問權(quán)限
          ?return?roles.some(role?=>?route.meta.roles.includes(role))
          ?}?else?{
          ?//?不存在meta或者是不存在meta.roles,則說明是通用模塊,直接放行
          ?return?true
          ?}
          }
          ?export?function?filterAsyncRoutes(routes,?roles)?{
          ?const?res?=?[]
          ?routes.forEach(route?=>?{
          ?const?tmp?=?{?...route?}
          ?if?(hasPermission(roles,?tmp))?{?//?相對路由數(shù)組的每一項進行訪問權(quán)限的判斷
          ?if?(tmp.children)?{
          ?//?如果存在children,則遞歸調(diào)用篩選函數(shù)
          ?tmp.children?=?filterAsyncRoutes(tmp.children,?roles)
          ?}
          ?//?將處理好的路由配置放入到res中
          ?res.push(tmp)
          ?}
          ?})
          ?return?res
          }

          最后回到/permission.js 文件中

          const?accessRoutes?=?await?store.dispatch('permission/generateRoutes',?roles)
          //?這里的accessRoutes就是篩選之后的路由,
          //?最后通過route.addRoutes將constRoutes和accessRoutes進行合并,生成最終的訪問路由
          router.addRoutes(accessRoutes)

          擴展-按鈕權(quán)限

          路由權(quán)限控制基本流程已經(jīng)分析完,接下來我們也來看看項目里的按鈕權(quán)限控制的實現(xiàn),實現(xiàn)也比較簡單。

          基本用法
          "['admin','editor']">

          import?store?from?'@/store'
          ?function?checkPermission(el,?binding)?{
          ?const?{?value?}?=?binding
          ?//?從store中拿到我們訪問接口后,取到用戶角色信息
          ?const?roles?=?store.getters?&&?store.getters.roles
          ?if?(value?&&?value?instanceof?Array)?{?//?判斷傳入的值是不是數(shù)組,規(guī)范化傳值
          ?if?(value.length?>?0)?{
          ?const?permissionRoles?=?value
          ?//?只要傳入的permissionRoles中,包含了roles其中的一個值即可,則代表有權(quán)限
          ?const?hasPermission?=?roles.some(role?=>?{
          ?return?permissionRoles.includes(role)
          ?})
          ?//?沒有權(quán)限則進行刪除,不展示。
          ?//?v-permission具體實現(xiàn)可以根據(jù)業(yè)務(wù)場景進行修改
          ?if?(!hasPermission)?{
          ?el.parentNode?&&?el.parentNode.removeChild(el)
          ?}
          ?}
          ?}?else?{
          ?throw?new?Error(`need?roles!?Like?v-permission="['admin','editor']"`)
          ?}
          }
          ?export?default?{
          ?inserted(el,?binding)?{
          ?checkPermission(el,?binding)
          ?},
          ?update(el,?binding)?{
          ?checkPermission(el,?binding)
          ?}
          }

          總結(jié)

          • 存在 token
            • 如果是管理員則對所有模塊具有訪問權(quán)限
            • 非管理員,需要對異步路由進行篩選,通過遍歷異步路由,并通過 meta.roles 與用戶信息比較,判斷用戶是否具有訪問權(quán)限
            • 存在用戶角色信息,則說明該用戶的最終可訪問的路由已經(jīng)生成,可以直接放行
            • 不存在用戶信息
            1. 調(diào)用獲取用戶信息接口,獲取到用戶信息, 將用戶信息存放到 vuex 中
            2. 判斷用戶角色
            3. 將最終的可訪問路由存放到 vuex 中,最后通過 router.addRoutes,整合最后的路由配置列表
          • 不存在 token
            • 如果訪問路由在白名單下,則直接進行訪問
            • 訪問路由不存在白名單下,則重定向到登錄頁面 path: /login?redirect=/xxx,登錄成功后則跳轉(zhuǎn)到/xxx 對應(yīng)的頁面

          專注分享當(dāng)下最實用的前端技術(shù)。關(guān)注前端達人,與達人一起學(xué)習(xí)進步!

          長按關(guān)注"前端達人"

          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男人天堂视频网站 | 亚洲精品国产精品乱码不卡√香蕉 亚洲日韩一区二区三区四区丨高清 | 五月婷丁香综合 | 成人大片在线播放 | 围产精品久久久久久久妞妞 |