權限設計的雜談
這篇文章的定位,不是宣傳某個框架,僅僅之是梳理一下有關權限方面的一些想法和最近項目中的一些探索過程。 咱們主要想解決一下問題。前端
- 什麼是權限,程序員理解的權限和客戶所理解的權限是否是一致的。
- 權限的劃分原則,權限究竟是根據什麼原則進行組合的。
- 角色是用戶與權限之間的必要的關係嗎?角色到底承接了什麼做用。
- 如何進行合理的表設計。
- 安全框架。
1.什麼是權限
在不少與開發者也好,與客戶也好,溝通的過程當中咱們不少次提到了權限,可是權限具體的含義每一個人理解的含義都不明確,這樣很容易形成雙方信息不對稱,有的人就只是把權限理解成某個頁面的是否可訪問,可是有的人卻理解成其餘的東西。因此咱們要完全的定義一下權限是什麼。java
權限究竟是名詞屬性仍是動詞屬性,仍是名詞、動詞屬性均包含,這對於權限的含義很重要。若是是名詞屬性的話,那麼它應該是有具體的指代物;若是是動詞,則應該具備行爲表示。程序員
- 權限的名詞屬性:api接口、頁面、功能點。
- 權限的動詞屬性:可操做、不可操做。
那麼咱們如今來看,其實權限是名詞、動詞屬性,它必定是表達了兩層含義。即控制的對象、操做。數據庫
- 例如:權限A表示頁面A的可訪問。
- 例如:權限B表示頁面B可訪問且頁面內的功能b不可以使用
- 例如:權限C表示接口C不可調用。
- 例如:權限D表示頁面D可訪問,且接口D可訪問。
那麼進一步的說明,權限能夠表示單個控制的對象的操做集合,也能夠表示多個控制的對象的操做集合。而這二者的取捨則是有設計人員決定的。後端
一句話總結權限的含義:what(若干元素)進行how(若干操做)api
2.權限的劃分原則
咱們瞭解了權限的具體含義以後,接下來就是用的問題,咱們該如何去使用權限,如何將系統中的操做元素進行一個組合,這個我借鑑網上的一篇文章來解釋。劃分原則能夠按照「最小特權原則」和「數據抽象原則」。安全
- 我先舉一個反例,我把系統中全部的元素和操做都組合成一個權限。一個用戶擁有這個權限就至關擁有了系統全部的功能,實際上這確定是不行的,用戶在一套系統中必定有他不容許操做的內容,哪怕是超級管理員也可能會有不能操做的元素,那麼最大化權限則是行不通,由於不符合常理。
- 據此,咱們就把權限再進行一個拆分,按照業務模塊進行拆分,可是這實際上也是不行的。就好比系統中的財務模塊,假定模塊中含有報銷頁面和申報頁面,若是按照模塊進行拆分,那麼確定有用戶同時包含了兩個互斥功能。
- 根據1和2,咱們須要按照最小化進行權限劃分。可是這個也是值得商榷的,由於不一樣系統,最小的權限劃分對於提供的功能來講,劃分的角度也是不一樣的。
- 「最小特權劃分」從某個程度上來講決定了控制的對象 ,而數據抽象原則是是決定了操做。
- 數據抽象從字面的意思來看,其實很難理解究竟是什麼意思。一般咱們口頭上說最多的是CRUD增刪查改,這實際上就是數據抽象的一種,咱們能夠理解成元素操做許可權的意思。
- 可是CRUD並非數據抽象的所有,增刪查改用於單實體,基本是沒問題的,可是在構建關係上,實際上是不夠用的,例如任免某個經理管轄某個部門,從業務表面而言它修改了經理的管轄範圍。可是從代碼底層構建上來講,它屬於在經理和部門間新增了一道關係,因此根據需求咱們須要再額外的增長一類許可權「任免許可」,這一類型的擴展則須要根據系統實際的業務狀況進行劃分。
「最小特權」和「數據抽象」分別決定了權限中控制的對象和操做,可是這裏面還差了一個角度,則是現階段很是廣泛的先後端分離的權限劃分的問題。框架
- 先後端分離下的服務端,本質而言只是提供接口的或者rpc服務等其餘資源服務的服務提供方。
- 服務端能提供的權限的鑑權機制的對象:接口服務(api或者其餘形式的服務)不包含前端的頁面或頁面中的功能點。
- 前端或移動端的頁面元素的控制和鑑權實質上不禁服務端控制。
- 服務端能夠單獨的控制服務的權限。
- 服務端的服務對象是前端、移動端、第三方客戶端,提供的服務是接口服務。
在先後端已經分離的狀況下,服務端對於前端而言只是接口的提供者,但無權干涉前端頁面的展現,服務端對於前端而言,能提供的是僅鑑權服務的接口而已,可是頁面的構成,頁面的欄目菜單或頁面內的功能點的構成均由前端單獨完成的。前後端分離
- 前端的鑑權包含頁面的可訪問,和頁面上的某項功能按鈕是否能夠操做。
- 前端和移動端的服務對象是用戶,提供的服務是可視化的頁面。
先後端的服務對象的責任劃分清晰以後,咱們就不會混雜權限的歸屬的問題,在過去先後端沒分離的狀況下,頁面自己就是服務端的一體,就沒有這方面的問題。雖然分清楚了各端本質提供的服務的狀況,可是先後端分離的權限劃分中仍有新的問題。運維
- 由於服務端和前端的鑑權對象不一致,服務端只能鑑權到api接口,那麼是否將api接口和前端的頁面乃至頁面功能點進行數據庫表與表層面的綁定關係。
- 若是進行了進行了表與表之間的綁定關係,那麼整個權限系統的維護量,是否能在能承受範圍以內。
- 若是不進行表與表之間的綁定關係,前端頁面在操做功能的時候,服務端如何鑑權頁面調用的api接口是否在用戶可操做的權限以內?
其實上面的問題則須要一個取捨,要麼增長運維成本嚴格控制前端調用api接口的關係,偏重服務端的接口服務鑑權。要麼是給api接口和前端頁面及功能點再提供一個通性的邏輯判斷處理,如:頁面及調用的功能點屬於某個業務模塊的操做許可,而頁面觸發的接口也恰好是這個業務模塊的操做許可,那麼鑑權經過,不然鑑權失敗。這種就是屬於側重前端對於用戶的控制,弱化了接口級的控制。
3.角色與權限的關係
經過1,2的描述,基本肯定了權限的定義和劃分一個權限的通用法則。用戶在系統中最終是經過權限來使用各類功能點,是否有必要在用戶和權限中間再額外的附加一個關係。在咱們如今的權限設計中,是增長了這樣一層關係的,就是角色。
- 減小操做層面的重複性。角色其實就是一組權限的集合,是權限集合的更高級抽象,爲了便於運維和實際管理,經過角色的賦予,替代了權限賦予用戶的繁瑣性,在一套系統中,廣泛狀況都是權限的數量多於角色的數量。
- 權限是控制對象和操做集合,它自己不存在任何狀態,可是在賦予在用戶身上則擁有了狀態,好比權限A中容許用戶訪問頁面A,權限B容許用戶訪問頁面B,權限D運行用戶訪問B頁面,可是不容許訪問A頁面。那麼這層關係的維護在角色層面的話,會更加清晰,也就是說自己角色具備權限集合組裝的策略問題,對於互斥的權限有不一樣的方案處理。(權限中沒有某個操做和權限中禁止某個操做,是兩個不一樣的角度,不能混爲一談)
- 由於權限的可能存在互斥性,在實際業務中也會引起角色的互斥性,舉一個現實中的案例來解釋互斥性:張三是軟件部的負責人但由於工做的特殊性也一樣隸屬於業務部的普通員工,咱們設定負責人是能夠要求人事部門給本部門進行招聘的,在實際的狀況中,張三能給軟件部招聘新員工,可是不能給業務部招聘員工。咱們把這個案例運用在系統中,張三則是擁有負責人和普通員工兩個角色,可是招聘的功能若是不加以控制,則會發生張三給業務部招人的結果。因而爲了解決角色的這類問題,引入了職責劃分的方案。
- 職責劃分分爲:靜態、動態。所謂靜態職責劃分則是在角色建立之初就已經肯定了角色的職責內容。動態職責劃分是系統運行過程當中對用戶已有的角色進行控制,例如:某些角色不能共存在用戶身上(互斥)、角色或角色的分配數量限定(控制用量)、角色與角色同時只能激活一個進行使用(時刻惟一)。
引入角色的概念後,實際上這已是一個比較完整的RBAC的權限設計的模型了。
4.數據表的設計思路
根據3的結論,實質上已經有了一個基礎的表設計的雛形。在這裏就有一些值得注意的點。
- (1)問:權限表是否有必要存在?
- (1)答:這個要結合系統的實際使用場景進行考慮,若是系統中的權限的對象很單一,好比只有頁面,或者只有api接口的話,其實權限表無關緊要。增長權限表反而會致使初始化項目權限的工做量增長。可是若系統中的權限對象是多個,那麼權限表的存在就有了更深層次的意義。在權限對象是多個的狀況,權限表的存在就是爲了更好更抽象的組合「最小特權」及「責任劃分」的操做對象。同時,一旦系統中的操做對象增長了,只須要給權限表增長一個對象表和關係表就能夠了。這樣易於擴展。
- (2)問:api接口和頁面其實是沒有關係的,可是在鑑權活動是有關係的,頁面若和api沒有一點綁定聯繫的話,服務端接口調用的時候則要麼攔截掉全部指定的接口(頁面和api接口沒綁定的話,則頁面的接口調用都不能成功),服務端接口徹底不攔截接口,也會不安全,可是api接口和頁面功能在表結構層面的綁定會產生運維的大量工做成本,如何更好的設計。
- (2)答:在權限如何劃分中已經提過了這一點,在表結構中,咱們能夠增長一張業務模塊表和操做表(也能夠在數據字典表中增長這兩類數據),咱們能夠在頁面和功能點鐘 綁定業務模塊和操做表關係,在api接口的代碼層面去綁定業務模塊和操做,在邏輯上綁定關係,解耦表結構之間的關係,那麼能夠在必定程度上解決這一點,這樣作只會出現一種問題,那就是用戶訪問頁面的時候可調用的api接口會比實際可調用的接口數要多,可是前端權限管理會隱藏功能點,這樣就在可視化的程度上解決了這個問題。
5.安全框架
因爲咱們是基於RBAC的權限設計,現行java框架下最多見的就是shiro和Spring Security 。這兩個就是仁者見仁智者見智了,我二者都實用過。僅建議使用shiro的話,能夠更好的理解RBAC的設計思路,Spring Security 也是個不錯的框架,可是它涉及到的概念太多,並不利於初學者去了解最基本的權限設計。我在這隻在學習的角度上去比較這兩個框架,並無再其餘領域去作比較,也不去比較。
引用的文章
權限系統與RBAC模型概述[絕對經典]