Vueルート権限制御解析


前言
本人は会社で主に中楽屋システムの開発を担当しています。ルートと権限検査はとても重要で、最も基本的な一環です。実際の開発プロジェクトでは、ログインとルート権限の制御について、vue-element-adminというスタープロジェクトを参照して、それに基づいて業務を統合しました。これからはこのプロジェクトを例にして、ルート全体と権限検査の過程を詳しく分析します。
プロジェクト全体のディレクトリ構造
今日のテーマに入る前に、まずプロジェクト全体を整理します。
  • api:インターフェース要求
  • クラスets:静的資源
  • components:汎用コンポーネント
  • directive:カスタムコマンド
  • filters:カスタムフィルタ
  • icons:アイコン
  • layout:レイアウトコンポーネント(ページアーキテクチャコア)
  • router:ルーティング構成(ルーティング権限コアモジュール)
  • store:状態管理
  • スタイルファイル
  • utils:ツール方法
  • view:ページコンポーネント
  • permission.js権限管理
  • このプロジェクトに興味がある人は自分で勉強してもいいです。ルートの権限検査の機能以外にも面白い機能がたくさん含まれています。多くのことを学ぶことができると信じています。
    ルート権限制御ロジック
    ルーティング処理フローチャート

    ルート処理のソースコード分析
    私たちはまずpermission.jsファイルを見つけました。ここではグローバルルーティング・ガードを定義します。ルート権限の中で非常に重要なコアコードです。みんなが読みやすいように、ルートに関するコードだけを外しました。
    
    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') 
      //        /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')
       //        ,      
       const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
       //        (                 )
       router.addRoutes(accessRoutes)
       //     
       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=$jt.path、ここでのredirectパラメータは主にユーザ登録成功後にジャンプするためのページパスです。具体的な機能は/view/login/index.vueファイルの下にあります。
    
    watch: {
     $route: {
     handler: function(route) {
      const query = route.query
      if (query) { //      
      this.redirect = query.redirect
      this.otherQuery = this.getOtherQuery(query)
      }
     },
     immediate: true
     }
    },
    // methods  :
    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 {
      // ..
     }
     })
    },
    
    動的経路設定
    まずルートの定義を見てみます。/src/router/index.jsファイルの下です。
    
    export const constantRoutes = [ //            ,        
     //       
    ]
    export const asyncRoutes = [ //        meta.roles       ,        
     {
     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       
     },
     // ...
     }
    ]
    
    動的にルートを追加する場合、基本的にはユーザーの役割情報に基づいてasyncRoutesルート構成配列にルートフィルタをかけ、対応するルートを見つけ、constantRoutesと統合して最新のルートを生成する。
    ルーティング論理図を動的に追加

    動的ルートのソースコード分析
    コードエントリ:permission.js
    
    const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
    
    permission/generate Routes方法入口ファイル:/store/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,           
      accessedRoutes = asyncRoutes || [] 
      } else {
      //        ,         roles         
      accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      //          vuex 
      commit('SET_ROUTES', accessedRoutes)
      // resolve  
      resolve(accessedRoutes)
     })
     }
    }
    
    非同期ルートをスクリーニングし、最終的な結果をvuexに保存し、結果のresoveを外に出します。
    
    export function hasPermission(roles, route) {
     if (route.meta && route.meta.roles) { //     meta.roles
     //   meta.roles               ,         
     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)) { //                    
      if (tmp.children) {
      //     children,         
      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)
    
    拡張→ボタンの権限
    ルート権限制御の基本的な流れはすでに分析済みです。これからはプロジェクトのボタン権限制御の実現を見に来ます。実現も簡単です。
    
        
    <div v-permission="['admin','editor']"></div>
    import store from '@/store'
    
    function checkPermission(el, binding) {
     const { value } = binding
     //  store          ,        
     const roles = store.getters && store.getters.roles
    
     if (value && value instanceof Array) { //            ,     
     if (value.length > 0) {
      const permissionRoles = value
      //      permissionRoles ,   roles        ,      
      const hasPermission = roles.some(role => {
      return permissionRoles.includes(role)
      })
      //          ,   。
      // v-permission                
      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)
     }
    }
    
    
    締め括りをつける
    存在する
           ユーザキャラクター情報が存在すると、そのユーザの最終的なアクセス可能なルートが生成されたことを説明し、直接に実行することができる。
           ユーザ情報が存在しません。
                  1.ユーザ情報インターフェースを呼び出し、ユーザ情報を取得し、ユーザ情報をvuexに保存する。
                  2.ユーザーの役割を判断する
  • 管理者であれば、すべてのモジュールにアクセス権限を持つ
  • 非管理者は、非同期的なルーティングをスクリーニングする必要があり、非同期的なルーティングを介して、Meta.rolesを介してユーザ情報と比較し、ユーザがアクセス権限を持っているかどうか判断する

  •               3.最終的なアクセス可能なルーティングをvuexに保存し、最後にrouter.addRoutesを通じて最終的なルーティング構成リストを統合する。
    tokenは存在しません
           アクセスルートがホワイトリストであれば、直接にアクセスします。
           アクセスルートに白いリストが存在しない場合は、ログインページのpath:/login?redirect=/xxxにリダイレクトし、ログインが成功したら/xxxに対応するページにジャンプします。
    以上がVueルートの権限制御解析の詳細です。Vueルートの権限制御に関する資料は他の関連記事に注目してください。