前端權限控制方案

在作商家後臺管理系統時,做爲前端一般會設計到大量的權限控制問題,按照細粒度歸歸類大體能夠分類如下三類javascript

  1. 頁面權限前端

  2. 模塊權限-頁面區塊(組件)是否顯示java

  3. 元件權限-組件內元素是否顯示git

以往的處理方式

後端會將用戶權限數據同步注入到VM模板中或者前端發送異步請求取到權限數據,數據消費場景通常都散落在代碼的角角落落。github

// 僞代碼
    render(){
        return {window.permission?<Component/>:null}
    }

    render(){
        return <Component>{this.props.permission?<Button>刪除</Button>: null}</Component>
    }

用這種方式實現的代碼,執行上沒有問題,也達到了業務的需求。可是隨着代碼量的遞增,代碼變得難以維護,特別是新接手的同窗,簡直是一場噩夢。express

React體系下的實現方式

頁面權限、模塊權限、元件權限三種前端權限表現形式對應不一樣的管理策略。後端

頁面權限

對於傳統的多頁應用,頁面權限控制不須要前端關心,後端路由作一層控制。在SPA架構的前端應用中,咱們的思路是將全部的前端路由配置在後端,對於不一樣角色的用戶,後端把路由列表吐給前端註冊。閉包

模塊權限、元件權限

對於這兩類權限控制的事就所有須要交給前端處理了,大體思路是將系統中用戶散落的權限統一配置,經過HOC包裝一下React組件,提供劫持渲染和權限透傳的能力。架構

統一管理權限registerAuthRules

應用的全部權限配置會被統一配置在一個閉包中,權限的值支持後端同步吐出,也支持每次異步獲取(利用Promise實現)app

// 僞代碼
export const AUTH_RULES = {
    'isX1': window.isX1 === '',
    'isX2': window.isX2 === '',
    'isX3': () => {
        return new Promise((resolve, reject) => {
            resolve(result); // resolve的參數只能是true或者false
        })
    },
};

registerAuthRules(AUTH_RULES);

權限規則表達式

權限列表中配置的只是顆粒度最細的單個權限。在實際業務需求中,咱們常須要根據權限格則組合結果,決定是否顯示。好比ComponentA的顯示條件是isX1 && isX2 或者 isX1 || isX3。
這裏須要引入權限規則表達式的概念。How to compute?
第一步:利用詞法分析器解析出表達式中有多少個權限變量。利用esprima能夠輕鬆取到
第二步:計算每一個變量對應的權限值
第三部:計算規則表達式,由於權限規則有多是異步或者的,這裏將每一個格則包裝成Promise對象,利用Promise.all作統一返回,在成功的回調函數中經過New Function的方式計算字符串表達式的結果

// 計算表達式相關代碼
function getExpressionValue(expression, data) {
    const codes = [];
    for (const key in data) {
        if (data.hasOwnProperty(key)) {
            const value =
                typeof data[key] === 'string' ? `"${data[key]}"` : data[key];
            codes.push(`var ${key} = ${value};`);
        }
    }
    codes.push(`return ${expression};`);
    return new Function(codes.join(''))();
}

如何使用

registerAuthRules

註冊權限規則列表,支持同步規則和異步規則
參數:

  • rules {Object} 應用權限規則MAP

registerComponentRules

註冊組件顯示規則,根據組件displayName配置組件所需權限列表
參數:

  • rules {Object} 組件權限規則MAP

調用查看

export const COMPONENTS_RULES = {
        ComponentA: 'isX1',
        ComponentB: 'isX1 && isX2',
    };
    registerComponentRules(COMPONENTS_RULES)

Auth HOC函數

參數:

  • options {Object} 組件權限規則MAP

  • options.placeholder {Component} 組件隱藏時的佔位節點;默認爲noscript

  • options.initialHide {Boolean} 當存在異步權限規則時,組件是否先默認隱藏;默認值爲true

  • options.rules {Object} 配置組件須要權限規則集合,做爲props屬性$auth傳遞給組件

1. 組件級別權限控制

根據WrappedComponent.displayName判斷組件是否有權限

class Component{
  // ...
}
Component.displayName = 'ComponentA';

const Authed_Component_1 = Auth({
  placeholder: <p>無權限的佔位節點</p>
})(Component)
2. 組件內部權限控制(權限屬性模式)
class Page{
  render(){
    const {$auth} = this.props;
    return (
      <div>
        { $auth.isShowDeleteBtn && <p>刪除</p> }
      </div>
    )
  }
}
// 權限校驗條件與權限屬性,組件內容沒有校驗邏輯
const Authed_Page = Auth({
  rules: {
    'isShowDeleteBtn': 'isVip'
  }
})(Page);

代碼實現[hoc-auth]https://github.com/amibug/hoc...

相關文章
相關標籤/搜索