最近正在研究權限系統,公司也正好用到了,看到了有贊技術博客講解很全面,就記錄了下來,方便自己查看,如有侵權,請聯繫作者刪除。
權限系統的概念和設計
計算機世界中的許多事物是現實世界的一個陰影,現實中所見的許多模式/概念在計算機世界裏都能找到。曾記否,QQ 裏隱身對她可見,怕她看不見,下線又上線,卻依舊被視而不見;曾記否,親密無間的戀人們,分手後變成了最熟悉的陌生人,悲痛傷心之餘,微信、電話、 QQ 拉黑。這些案例,都是計算機權限系統對現實世界的一個映射,你對女神隱身可見,實際上是賦予了她可以看到你的隱身狀態(真實狀態)的權限,當然你也賦予了人家傷害你的權限;戀人們把對方拉到了黑名單用戶組,這樣一來,他們就看不見相互動態,成爲最熟悉的陌生人;從此,從你的全世界路過。
RBAC
上面例子,我們可以抽象出這樣的模式:“ Who 對 What(Which)進行 How 的操作” 。例如,戀人們的例子,在你拉黑對方後,在朋友圈中你(Who)將看不到(How)對方的消息(What)。這是一個經典的 RBAC(基於角色的權限訪問控制)權限模型。RBAC 認爲權限授權實際上是 Who、What、How 的問題。在 RBAC 模型中,Who、What、How 構成了訪問權限三元組,也就是“Who(權限的擁用者或主體)對 What(Which)(權限針對的對象或資源)進行 How(具體的權限)的操作”。
RBAC 模型引入了“角色”的概念。所謂“角色”就是一個或一羣用戶在系統中可執行操作的集合,它是一個用戶的集合,又是一個授權許可的集合。通過將角色指派給用戶,爲角色賦予權限的方式,使用戶和權限通過角色間接相聯繫。RBAC 基本模型如圖所示:
在 RBAC 中,用戶與角色之間、角色與權限之間都是多對多的關係。會話是一個用戶對多個角色的映射,此時的用戶權限可以爲激活角色權限的並集。RBAC 對資源授權管理過程分爲兩個部分,首先實現訪問權限與角色相關聯,然後再實現角色與用戶相關聯,從而實現了用戶與訪問權限的邏輯分離。
權限系統 SAM
SAM 權限系統模型設計
RBAC 模型不同於強制存取控制以及自由選定存取控制直接賦予使用者權限,是將權限賦予角色。在 RBAC 中,權限與角色相關聯,用戶通過成爲適當角色成員而得到這些角色的權限,角色可依新的需求和系統的合併而賦予新的權限,而權限也可根據需要而從某角色中回收。RBAC 相對於傳統訪問控制更爲中性且更具靈活性的存取控制技術。從一家零售店鋪員工角色管理角度看,設置角色是爲了完成各種工作而創造,員工則根據它的責任和資格來被指派相應的角色,員工應該可以很容易地從一個角色被指派到另一個角色。因此,零售選擇了基於 RBAC 模型來實現權限系統解決商家們管理員工角色問題。
依據 RBAC 模型思想,SAM 權限系統業務模型設計爲員工管理和權限管理兩部分,員工管理主要指管理員工以及爲員工指派角色,權限管理主要指管理菜單、頁面、按鈕、API 等資源,通過定義最基本的業務功能點作爲權限點,實現管理角色對資源主體的請求,構成“用戶-角色-權限-資源”的授權模型。
下面是 SAM 權限系統模型中的一些通用語言:
- 員工:角色的載體,權限的實行者
- 角色:角色是權限集進一步映射。業務系統可動態管理角色,各業務爲方便用戶使用可提供給默認角色列表,滿足不同的員工權限
- 權限點:全局唯一的用來表示某一個功能點對應的權限的狀態
- 功能點:邏輯上定義的用來描述系統資源的最小基本單位,每一個功能點都對應唯一一個權限點
- 功能集(權限集):即功能點的集合,有一組功能點按照特定格式進行組合
- API:請求系統資源的通道和動作,擁有功能集屬性
- 菜單:將系統資源組織後展示給請求者的入口,擁有功能集屬性
- 頁面:被當做一種特殊的菜單,擁有 URL 屬性
- 按鈕:頁面中更細粒度的資源入口,被當作一種特殊的菜單
SAM權限系統模型的實現
在傳統的 RBAC 模型中,通常通過一張關係表來保存角色與權限集的對應關係,實現權限與角色相關聯。可以預見的是,隨着零售業務的不斷髮展會積累下不計其數的功能點,導致關聯表的數據極難維護和使用。SAM 權限系統利用進制轉換的策略解決了這個問題 ,同時提高了存儲效率以及權限判定效率。一個基本類型爲 Long 的十進制數字,它也可以看做是由 64 位 0 或 1 組成的二進制。在 SAM 系統模型設計中,每一個功能點定義爲一個權限點,該權限點由 idx 和 pos 兩個屬性確保是全局唯一的權限點。idx 表示第幾個 Long 型空間,pos 表示 Long 型對應的二進制數中所處的位置,64 位長度即可代表 64 個不同能功能點。當 64 位滿時無法再容放更多的功能點,這時 idx 屬性會自增,重新申請一個 Long 型空間。如此一個 64 位的 Long 數字,通過 0 或 1 的組合,即可表示最多對 64 個不同的功能點所擁有權限的狀態描述。
例如:權限集{1}表示擁有 idx=0, pos=0 對應功能點的權限,權限集{-1,1}表示擁有idx=0,pos∈[0,1,2,…,63]與 idx=1,pos=0 對應功能點的權限。
SAM 權限系統將資源與所代表的功能點的關聯關係通過進制的方式管理起來,採用計算機進制的思想,抽象出功能集換算公式來完成資源與二進制之間的映射,以及角色與二進制的映射。
權限集換算公式:{(idx0,pos0),(idx0,pos1)…(idxN,posM)} => {Long0,Long1…LongN}
SAM 權限系統同樣通過進制思想實現“ Who 對 What 進行了 How 的操作”,角色請求某個資源(菜單/API)時,通過權限校驗計算公式——進制按位“與”運算操作的思想(見下)得出該角色是否擁有訪問資源的權限。採用進制來實現運算,權限判定的效率會變得更加的高效。例如,一個倉管在點擊一個商品庫存菜單時,背後的權限校驗計算公式,其實是將角色的權限集與資源的權限集進行按位與計算,任意一對序號爲 idx 的 Long 算得不爲 0,即兩集合有公共的功能集,認爲該角色擁有對資源訪問的權限。
權限校驗計算公式:{Long0,Long1…LongN} & {Long0,Long1…LongM}
SAM 權限系統模型的實現遵循 RBAC 模型中的最小權限原則,責任分離原則和數據抽象原則三大原則,通過最小權限原則可以將角色配置成其完成任務所需要的最小的功能集;有了責任分離原則可以通過調用相互獨立互斥的角色來共同完成敏感的任務而體現,比如要求一個倉管和商品管理員共同參與一個商品。數據抽象則可以通過權限的抽象來體現,如倉管操作商品發貨,庫存管理等抽象權限,而不用操作系統提供的典型的讀、寫、執行權限。
SAM 權限系統架構
零售通過 PC、App 和 Pad 來滿足不同商家的終端需求,因此 SAM 權限系統需要滿足零售不同客戶端權限業務場景,同時也要支持微商城產品權限業務。SAM 權限系統採用微服務的方式對外提供服務,採用分佈式分層架構實現,主要包括客戶端和服務端兩部分,客戶端以輕量的方式嵌入在業務系統,提供給不同業務系統實現角色訪問資源的控制;服務端通過提供 Dubbo 服務,Nova 服務跟客戶端進行交互。服務端主要對員工,菜單,角色,API,功能點進行數據管理。SAM 作爲基礎服務,每天的請求量巨大,通過 Redis 緩存來解決性能問題,選用 Druid 作爲數據庫連接池,管理着數據庫的連接以及釋放。同時,通過對接天網監控平臺來觀察系統運行狀態,提高系統的穩定性。
有贊零售系統基於 SAM 實現的角色對於資源的訪問控制主要是 API 校驗和菜單渲染,任何一家零售店登入有贊零售系統後,點擊頁面中的某一個菜單或者頁面元素(按鈕,鏈接…),都會進行菜單渲染以及 API 接口的校驗。由於兩部分調用量巨大,同時不同的客戶端請求量不同,防止相互之間干擾,因此將菜單渲染,API校驗等能力在不同的客戶端中各自實現。
菜單渲染
SAM 通過客戶端的方式進行接入,菜單渲染在客戶端一側進行。目前 SAM 已經提供了 php/node js 兩套客戶端,供 web 層進行接入和渲染。
菜單渲染的過程可以分爲三點:
一、結點定位
按照系統功能的劃分,菜單通常以一棵樹的形式進行展現。以零售 PC 後臺爲例,所有在頁面中展示的元素,都認爲是一種菜單,這樣的菜單元素包括:菜單、頁面、按鈕。在後臺訪問時,用戶停留的菜單通常是頁面,頁面有一個全局唯一的屬性:URL,往上:可以通過父菜單找到根結點,往下,頁面下可能包含一些子菜單——按鈕。因此 SAM 只需要根據當前請求的 URL,即可在後臺菜單樹中定位到唯一的頁面菜單,同時獲得該菜單的結點路徑以及擁有的按鈕。
二、權限計算
我們已經獲得了用戶的角色權限和完整的菜單樹,根據每個菜單結點的權限集,可以計算出當前用戶對結點的訪問權。根據計算結果,客戶端對菜單可以進行區分渲染,比如:用戶通過拼 URL 訪問一個無權限頁面時會提示非法,無權限訪問的菜單和按鈕會自動置灰不可點擊。
三、屬性傳遞
默認菜單不具備 URL 屬性。菜單的 URL 屬性通過子菜單的 URL 傳遞生成,SAM 會選擇第一個有權限的子菜單的 URL 作爲父結點的屬性,並逐級傳遞到一級菜單。
API 權限校驗
零售系統中除了菜單外,API 是另一種被請求的資源類型。API 校驗是除了菜單渲染外另一道權限控制的保障。通過卡門( API 網關)的 API 請求轉發到具體業務系統時,嵌入在業務系統中的 SAM API 校驗客戶端會首先通過上面的權限校驗計算公式對該角色是否具有權限訪問這個 API 進行判定,若權限校驗通過則執行後面業務邏輯。
具體流程如下圖所示
API 權限校驗的僞代碼實現:
AUTHPERM_ERROR(231000401,"您沒有權限執行該操作!")
# 織入點
@Before("@annotation(com.youzan.sam.common.Auth)")
# 切面處理方法
def handle(JoinPoint pjp):
# 可以啓動時或者運行時控制該開關是否對API進行權限校驗
if(!enable):
return
# 權限校驗結果包裝對象
def pass=checkPermission()
# 權限校驗執行成功
if (pass.isSuccess()):
# 權限校驗通過
if(pass.getData().get("isSuccess")):
return
# 權限校驗不通過
else:
throw new BusinessException(AUTHPERM_ERROR.getCode,AUTHPERM_ERROR.getMessage());
# 權限校驗執行失敗
else:
throw BusinessException(pass.getCode(), pass.getMessage())
# 權限校驗方法
def checkPermission():
# 判斷是否需要走權限校驗,對於某些內部調用可以直接跳過
{...}
# 獲取卡門(API網關)隱式參數,運用了dubbo的隱式傳參的能力
def kdt_id=RpcContext.getContext().getAttachment(Constants.KDT_ID_KEY)
def admin_id=RpcContext.getContext().getAttachment(Constants.ADMIN_ID_KEY)
def service = RpcContext.getContext().getAttachment(Constants.SERVICE_KEY)
def method = RpcContext.getContext().getAttachment(Constants.METHOD_KEY)
def version = RpcContext.getContext().getAttachment(Constants.VERSION_KEY)
# 上述參數的校驗
{...}
# 通過StaffPermServiceProxy獲取角色的權限集
def staffPerm=StaffPermServiceProxy.getStaffPerms(adminId, kdtId)
# 通過APIPermServiceProxy獲取API的權限集
def apiPerm=APIPermServiceProxy.getServicePerms(service, version, method)
# 運用權限校驗計算公式判定該角色是否可以訪問此API
{...}
# 返回結果
return pass
API 權限校驗流程可以總結如下:
- 業務方在對應需要權限校驗的 API 上標註 @Auth 註解,Spring 框架會在初始化創建業務 bean 的時候,掃描該 bean 是否有 @Auth 註解標註的方法,對於有 @Auth 註解標註的,會創建代理類,然後會將該權限切面織入到代理類中;
- 業務調用有 @Auth 註解標註的方法時,會執行該權限校驗切面邏輯,首先檢查權限校驗開關,判斷是否需要權限校驗,該開關可以在運行時動態設置;
- 如果需要,再調用 AuthService 的權限校驗方法,AuthService 會根據店鋪 ID 與用戶 ID 從 SAM 服務端獲取員工角色權限信息,根據卡門(API 網關)隱式參數中 service,method,version 去 SAM 服務端獲取對應 API 權限(相對於在對應 API 上直接標註權限點,這種方式更加的靈活,而且可以隨着業務 API 版本的升級,進行很方便的升級,同時結合卡門( API 網關)可以對 API 進行分流,不同的商家可以對應不同 API 的權限校驗。
- 在獲取到角色權限集和 API 權限集後,基於上面的角色與權限校驗邏輯進行權限校驗。校驗通過,則正式發起 API 請求。校驗不過,則提示無權限。
SAM權限系統抽象模型
產品在分析完需求後,將需求交由開發去完成。SAM 權限系統支撐着零售業務的同時,也支撐着微商城業務。 零售各個模塊就有不同的產品支撐着,爲了更好的滿足服務商家的需求,以及方便產品們的分析。SAM 權限系統可以抽象成如下模型,商家和產品可以從各自不同的視角,去對接 SAM 權限系統。例如,下圖所示商家想要一個運營的角色需要有新建商品,以及查看訂單的能力,同時需要一個收銀員只有查看訂單的能力。產品從自己的設計角度分析,對應的就是商品管理,訂單管理的模塊,對應的模塊下有對應的商品,訂單菜單,最後將角色的權限體現在頁面元素和 API 上,例如新建商品的按鈕,以及查看訂單的按鈕會呈現不同的渲染樣式;按鈕觸發對應的是與後端交互的不同 API,不同的角色具有 API 的不同執行能力
此文章源自於:https://segmentfault.com/a/1190000013787034#item-2