vue菜單權(quán)限驗證

在項目中,權(quán)限驗證與安全性非常重要,是一開始就必須考慮和搭建的基礎(chǔ)核心功能。我們所要做到的是:不同的權(quán)限對應著不同的路由,同時側(cè)邊欄也需根據(jù)不同的權(quán)限,異步生成。

思路

這里參考了vue-admin項目的代碼:vue-element-admin權(quán)限驗證篇,但是本項目的業(yè)務(wù)邏輯需求是每個頁面的權(quán)限是動態(tài)配置的,而vue-admin項目中是寫死預設(shè)的,不完全符合需求,故進行了改進(過去的一些項目中沒用addRoute,權(quán)限控制代碼里很多都是各種if/else的邏輯判斷,代碼相當?shù)鸟詈虾蛷碗s,維護起來相當?shù)睦щy,在vue2.2.0以后新增了router.addRoutes,就可相對方便的做權(quán)限控制了)。
前端會有一份公共的路由表,它表示了不需要權(quán)限,公共可訪問的權(quán)限,如登錄頁面、主面板等,同時存儲一份會根據(jù)權(quán)限變動的路由表,當用戶登錄之后,通過 token 獲取用戶的 role ,同時后端根據(jù)角色返回用戶可以訪問的菜單及內(nèi)容信息,最后前端動態(tài)算出其對應有權(quán)限的路由,再通過router.addRoutes動態(tài)掛載路由。
但這些控制都只是頁面級的,后端則會驗證每一個涉及請求的操作,驗證其是否有該操作的權(quán)限,每一個后臺的請求不管是 get 還是 post 都會讓前端在請求 header里面攜帶用戶的 token,后端會根據(jù)該 token 來驗證用戶是否有權(quán)限執(zhí)行該操作。若沒有權(quán)限則拋出一個對應的狀態(tài)碼,前端檢測到該狀態(tài)碼,做出相對應的操作。

實現(xiàn)步驟

  1. 創(chuàng)建vue實例的時候?qū)ue-router掛載,但這個時候vue-router掛載一些登錄或者不用權(quán)限的公用的頁面。
  2. 當用戶登錄后,獲取用戶可訪問的菜單name等信息,將信息和路由表每個頁面的需要的權(quán)限作比較,生成最終用戶可訪問的路由表。
  3. 調(diào)用router.addRoutes(store.getters.addRouters)添加用戶可訪問的路由。
    使用vuex管理路由表,根據(jù)vuex中可訪問的路由渲染側(cè)邊欄組件。
前端存儲的權(quán)限路由表:
    const clientAsyncRoutes = [
      {
        path: "/form",
        component: "Layout",
        name: "form",
        children: [
          {
            path: "index",
            name: "form-1",
            component: () => import("@/views/form/index"),
            meta: { title: "Form", icon: "form" }
          }
        ]
      },
      {
        path: "/form2",
        component: () => import("@/views/form2/index"),
        name: "form2"
      },
      {
        path: "/nested",
        component: "Layout",
        redirect: "/nested/menu1",
        name: "nested",
        meta: {
          title: "Nested權(quán)限測試",
          icon: "nested"
        },
        children: [
          {
            path: "menu1",
            component: () => import("@/views/nested/menu1/index"), 
            name: "nested-1",
            meta: { title: "Menu1" },
            children: [
              {
                path: "menu1-1",
                component: () => import("@/views/nested/menu1/menu1-1"),
                name: "nested-1-1",
                meta: { title: "Menu1-1" }
              },
              {
                path: "menu1-2",
                component: () => import("@/views/nested/menu1/menu1-2"),
                name: "nested-1-2",
                meta: { title: "Menu1-2" },
                children: [
                  {
                    path: "menu1-2-1",
                    component: () =>
                      import("@/views/nested/menu1/menu1-2/menu1-2-1"),
                    name: "nested-1-2-1",
                    meta: { title: "Menu1-2-1" }
                  },
                  {
                    path: "menu1-2-2",
                    component: () =>
                      import("@/views/nested/menu1/menu1-2/menu1-2-2"),
                    name: "nested-1-2-2",
                    meta: { title: "Menu1-2-2" }
                  }
                ]
              }
            ]
          },
          {
            path: "menu2",
            name: "nested-2",
            component: () => import("@/views/nested/menu2/index"),
            meta: { title: "menu2" }
          }
        ]
      }
    ];
登陸時后端返回的可訪問菜單數(shù)據(jù):
    const serverRouter = [
      {
        name: "form",
        children: [{ name: "form-1", content: ["aa", "bb"] }]
      },
      {
        name: "form2",
      },
      {
        name: "nested",
        children: [
          {
            name: "nested-1",
            children: [
              { name: "nested-1-1" },
              {
                name: "nested-1-2",
                children: [{ name: "nested-1-2-2", content: ["c", "d"] }]
              }
            ]
          },
        ]
      }
    ];

前端對數(shù)據(jù)進行遍歷和重組,最終遞歸生成動態(tài)路由數(shù)據(jù):

//生成路由表部分代碼
    function makePermissionRouters(serverRouter, clientAsyncRoutes) {
      const res = [];
      clientAsyncRoutes.map(ele => {
        for (let i = 0; i < serverRouter.length; i++) {
          const element = serverRouter[i];
          if (ele.name === element.name) {
            const tmp = deepClone(ele);
            if (element.content) {
              tmp.meta.content = element.content;
            }
            if (element.children) {
              tmp.children = makePermissionRouters(
                element.children,
                ele.children
              );
            }
            res.push(tmp);
          }
        }
      });
      return res;
    }
//生成的動態(tài)路由
   const Routes = [
      {
        path: "/form",
        component: "Layout",
        name: "form",
        children: [
          {
            path: "index",
            name: "form-1",
            component: () => import("@/views/form/index"),
            meta: { title: "Form", icon: "form", content: ["aa", "bb"] }
          }
        ]
      },
      {
        path: "/form2",
        component: () => import("@/views/form2/index"),
        name: "form2"
      },
      {
        path: "/nested",
        component: "Layout",
        redirect: "/nested/menu1",
        name: "nested",
        meta: {
          title: "Nested權(quán)限測試",
          icon: "nested"
        },
        children: [
          {
            path: "menu1",
            component: () => import("@/views/nested/menu1/index"), // Parent router-view
            name: "nested-1",
            meta: { title: "Menu1" },
            children: [
              {
                path: "menu1-1",
                component: () => import("@/views/nested/menu1/menu1-1"),
                name: "nested-1-1",
                meta: { title: "Menu1-1" }
              },
              {
                path: "menu1-2",
                component: () => import("@/views/nested/menu1/menu1-2"),
                name: "nested-1-2",
                meta: { title: "Menu1-2" },
                children: [
                  {
                    path: "menu1-2-2",
                    component: () =>
                      import("@/views/nested/menu1/menu1-2/menu1-2-2"),
                    name: "nested-1-2-2",
                    meta: { title: "Menu1-2-2", content: ["c", "d"] }
                  }
                ]
              }
            ]
          }
        ]
      }
    ];

這里我根據(jù) vue-router官方推薦 的方法通過meta標簽來標示改頁面能訪問的權(quán)限有哪些。如meta: {content: ["c", "d"] }表示該頁面用戶擁有查看c和d內(nèi)容區(qū)域的權(quán)限。
若需要查看所有用戶權(quán)限,如管理員編輯角色權(quán)限分配時,后臺只需返回 每個角色可訪問的菜單數(shù)據(jù)便可,結(jié)構(gòu)同登錄時相同,如:

    const data = [
      {
        username: "user1",
        role: "admin",
        route: [
          {
            name: "form",
            children: [{ name: "form-1", content: ["aa", "bb"] }]
          },
          {
            name: "form2"
          }
        ]
      },
      {
        username: "user2",
        role: "editor",
        route: [
          {
            name: "form",
            children: [{ name: "form-1", content: ["aa", "bb"] }]
          }
        ]
      }
    ];

管理員可以用樹形圖的方式勾選編輯權(quán)限,進行用戶的權(quán)限分配。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 完整項目地址:vue-element-admin系類文章一:手摸手,帶你用vue擼后臺 系列一(基礎(chǔ)篇) 前言 拖...
    7cd975786ccd閱讀 10,945評論 4 65
  • 使用全局路由守衛(wèi) 實現(xiàn) 前端定義好路由,并且在路由上標記相應的權(quán)限信息 全局路由守衛(wèi)每次都判斷用戶是否已經(jīng)登錄,沒...
    若邪Y閱讀 30,973評論 4 54
  • 學習總結(jié)vue后臺管理系統(tǒng) 后臺頁面的權(quán)限驗證與安全性是非常重要的,可以說是一個后臺項目一開始就必須考慮和搭建的基...
    尋心途閱讀 2,417評論 0 4
  • 古人云:將相頂頭堪走馬,公侯肚內(nèi)好撐船。自古及今,將相也好,公侯也罷,若心胸狹窄,沒有包容之心,難成大器。 在企業(yè)...
    醉春風0920閱讀 1,354評論 0 1
  • 呂布是戰(zhàn)國末年著名的商人,他還是一個政治家、思想家。當年由于大家的經(jīng)商手段非常的厲害,所以他們家的資產(chǎn)...
    王珈雨閱讀 1,245評論 0 0

友情鏈接更多精彩內(nèi)容