歡迎關注個人公衆號睿Talk
,獲取我最新的文章:javascript
在成熟的電商系統中,權限管理是不可或缺的一個環節。靈活的權限管理有助於管理員對不一樣的人員分配不一樣的權限,在知足業務須要的同時保證敏感數據只對有權限的人開放。筆者最近對系統的權限管理作了一次改造,在此分享一些經驗以供參考。前端
權限管理通常分如下 3 個基礎概念:java
它們之間的關係一句話就能說清楚:一個用戶能夠擁有多個角色,而一個角色能夠包含多個功能。好比一個員工能夠既有收銀員的角色,也有庫管員的角色。對於收銀員這個角色,能夠有開單收銀、查看訂單、查看會員信息等功能點。node
此外還有 2 個概念:webpack
它們之間的關係舉例來講明:
想象一個連鎖店的場景,某個門店的管理員具備查看營收的功能權限,和查看本身門店數據的數據權限;高級管理員一樣擁有查看營收的功能權限,而且有更高的數據權限,能夠查看全部門店數據的數據。web
下面咱們聚焦到前端領域,聊聊前端應該怎麼作權限設計。前端本質上只有 1 種權限類型:組件權限。爲了更好的理解和管理,又將組件權限拆分爲如下 3 類:segmentfault
每個權限最終都會落到權限點上。權限點能夠理解爲一個數據編碼,有這個權限點就說明有對應的功能權限。權限點的編碼要注意 2 點:後端
須要控制權限的地方,都要定義一個權限點,而後告訴後端。一個用戶全部的權限點會以數組的形式返回。判斷是否有權限就是從數組中匹配一個元素。下面以 React 爲例,聊聊具體的實現方式。數組
對於頁面的權限判斷,能夠在 React Router 的 onEnter 回調中判斷:緩存
// 編碼映射,下面的 getUrlCodeByName 會用到 export default { order_list: 'zaq0', // 訂單列表 order_detail: 'xsw1', // 訂單詳情 order_refund_list: 'cde2', // 訂單退款列表 order_refund_detail: 'vfr3', // 訂單退款詳情 order_deduct_modify: 'bgt4', // 訂單修改業績 }; function canAccessUrl(urlName) { const moduleCode = getUrlCodeByName(urlName); // 權限點通常是一個沒意義的編碼,爲了易於理解,前端作了一個編碼映射 return accesses.u.indexOf(moduleCode) > -1; // accesses.u 數組是後端返回的全部 url 權限點 } function routerOnEnterCheck(urlName) { return function routerOnEnter(nextState, replace) { if (!canAccessUrl(urlName)) { replace('/unauthorized'); } }; } ... { path: 'list', getComponent: loadAsync(() => import(/* webpackChunkName: "order" */ '../../order/List')), onEnter: routerOnEnterCheck('order_list'), } ...
對於菜單和組件的權限判斷,大致上長這樣:
// 用以緩存是否有權限訪問組件 const componentAccessCache = {}; /** * 檢查訪問組件的權限 * 一個組件可能會重複 render 屢次,而組件權限的數量可能會超多(上百個),所以將權限緩存起來以提升性能 */ function canAccessComponent(module, componentName) { if (!module || !componentName) { console.error(`canAccessComponent ${module} ${componentName} 缺參數`); } const key = `${module}.${componentName}`; let result = componentAccessCache[key]; if (result !== undefined) { return result; } const moduleCode = getComponentCodeByModuleAndName(module, componentName); // 權限點通常是一個沒意義的編碼,爲了更易於理解,前端作了一個編碼映射 result = accesses.c.indexOf(moduleCode) > -1; // accesses.c 數組是後端返回的全部組件權限點 componentAccessCache[key] = result; return result; } class SomeComponent extends PureComponent { ... render() { return ( ... { canAccessComponent('asset', 'buy_pay') && <Button type="primary" className="btn-buy" onClick={() => (buy(num))} > 當即訂購 </Button> } ... ) } }
組件權限點的判斷也能夠封裝成高階組件的形式,這樣看起來會舒服一點,但本質上是同樣的,再也不贅述。
權限點的獲取筆者放在了 node 端,經過全局變量的形式注入到頁面中,保證首屏的時候呈現的頁面是由權限點過濾過的。此外接口返回的權限點是一個一維數組,爲了加快前端檢索速度,在 node 端根據編碼規則將權限點分爲 3 類(菜單/頁面/組件),具體細節就不細說了。
本文介紹了權限管理的基礎知識,還結合 React 講解了前端權限控制的一些細節。技術方案比較簡單,真正麻煩的是每個權限點的定義及錄入,以及對現有系統的改造。改造過程當中能夠分模塊進行迭代,畢竟羅馬不是一天就能建成。