1 基于角色的權限設計(RBAC)
目的:判斷用戶能否操作資源
表現(xiàn):用戶心俗、角色、資源之間的連線
- 用戶蓉驹,特指“人”城榛。真實存在的個體
- 資源,用戶可以操作的接口态兴、菜單狠持、按鈕、文件诗茎、圖片等工坊,都是資源
- 角色献汗,抽象的層次很高敢订。簡單的場景,“用戶組”也可以表達這個模塊的含義
但罢吃,角色很復雜的:
約束類型 | 釋義 | 舉例 |
---|---|---|
自由組合 | 一個人可以同時擁有任意個角色 | 身兼多職 |
繼承 | 擁有parent node楚午,自動獲得All children roles 的權限 | 中國家長 |
互斥 | 互斥的角色,一個用戶只能擁有其中的一個 | 男尿招、女 |
運行時互斥 | 給一個用戶配置多個角色矾柜,但,運行時就谜,只能激活一個 | QQ怪蔑、360 |
限定個數(shù) | 一個角色,只能授予有限的用戶 | 主席 |
按順序演變 | 嬰兒丧荐、兒童缆瓣、少年... | |
運行時計算 | 判斷操作員是否資源持有者的上級 |
2 常見的設計方式
類別 | 示例 |
---|---|
層級 | 論壇系統(tǒng),超級管理員虹统、普通管理員弓坞、版主等隧甚,上級擁有下級的所有權限 |
角色 | 超市管理系統(tǒng),收銀員渡冻、開票員戚扳、倉管員等,用戶之間的地位是平等的族吻,分別對應不同的應用模塊 |
操作 | 博客系統(tǒng)帽借,文章的增刪改查等操作,都是資源 |
流程 | 公文系統(tǒng)呼奢,基層科員起草-->科長審批-->辦公室校對-->局長簽發(fā)宜雀。如果公文不在當前用戶的環(huán)節(jié),即使是局長也無權修改 |
分析:
- 層級和角色握础,都可以抽象為“角色”
- 在流程中辐董,不能把“公文”看做“資源”,節(jié)點才是禀综。節(jié)點(資源)-->角色-->用戶简烘,串起來就是權限
一句話:用戶、角色定枷、資源孤澎,很靈活的權限結(jié)構
2.1 脆弱的策略
有了好的架構,不代表落地的代碼也是靈活的欠窒。
例如覆旭,判斷一個用戶是否能查看項目報表,你的代碼或許是這樣的:
代碼 |
---|
如果岖妄,新增一個角色型将,似乎,整個流程都崩潰了荐虐。當然七兜,你可以加if...else
2.2 改進語義
還是上面的示例,換個方式呢福扬,你的代碼或許是這樣的:
代碼 |
---|
釋義 |
---|
是否允許當前用戶查看id=12345的報表腕铸? |
這樣的描述,我覺得铛碑,更符合“人”的思維習慣狠裹。
2.3 總結(jié)
判斷權限的代碼,可以寫死在代碼里汽烦,也可以放到配置文件涛菠、annotation、或是抽取到"service/util"里。無所謂啦碗暗。
我呢颈将,還是建議采用“改進的語義”。只要流程確定好了言疗,配套的驗證框架晴圾,你自己也會持續(xù)改進地嘛。即便寫死在代碼里噪奄,也是進步哦死姚,畢竟很明確嘛。
推導的內(nèi)容勤篮,僅供參考都毒。你也可以得出自己的結(jié)論。我相信碰缔,就算用了框架账劲,也會有各種不舒服。既然如此金抡,從一開始瀑焦,就抱著懷疑的態(tài)度,用實踐去求證梗肝。
代碼不會騙你榛瓮。最終,不但要用對框架巫击,還要改進
3 微服務與權限
微服務的架構禀晓,前后端完全分離。那么坝锰,在這種特定場景里粹懒,權限是怎么串起來的呢?
無論何時什黑,權限崎淳,始終關心的是用戶可以操作哪些資源:
- 推薦使用swagger2管理后臺的接口堪夭,直觀愕把、易測試
- 推薦使用react、angular等框架自定義“組件”
3.1 后臺接口&可讀&可寫
后臺開放的接口(API)森爽,需要指定http method恨豁。比如,Create爬迟,是write橘蜜;Get,是read。
創(chuàng)建接口的時候计福,讀或?qū)懙臋嘞蘧痛_定了跌捆。所以,讀象颖、寫佩厚,是不同的接口(資源)。
接口(資源)-->角色 --> 用戶说订,于是抄瓦,權限,又串起來了陶冷。
3.2 角色
管理員钙姊、子賬號,都是特殊的角色埂伦,他們都有操作后臺的權限
收銀員煞额、開票員、倉管員等沾谜,都跟特定的業(yè)務有關系
在具體的場景中立镶,你需要給某些模塊命名。這時候类早,如果媚媒,你覺得不管是“用戶”還是“資源”,都不適合涩僻,不妨用“角色”的概念封裝這些模塊缭召。
下面,著重分析下“動態(tài)角色”(先這么叫吧逆日。實現(xiàn)的時候要用到算法嵌巷,我覺得有點難。所以室抽,也沒寫全)
假設搪哪,“資源”屬于currentUser,操作員operator的角色是哪個呢坪圾?
這些“角色”晓折,都是動態(tài)的!J扌埂漓概!怎么辦呢?
- User表設計成樹形結(jié)構
id/pid/level病梢,分別對應id胃珍、父節(jié)點id、層級深度。于是觅彰,根節(jié)點={1,null,1}吩蔑,一級子節(jié)點={2,1,2}... - 訪問接口的時候,動態(tài)判斷操作員的角色
select case (level - ${operator.level})
when 1 then "immediateSubordinate"
when -1 then "immediateSuperior"
end as role
from t_user where id = ${currentUser.id}
動態(tài)查詢operator‘s role填抬,底層是在select tree node:
- 傳統(tǒng)的樹形結(jié)構id&pid哥纫,查詢的時候,會用到遞歸痴奏。層級越深蛀骇,效率越差。遞歸的SQL太復雜了读拆,我沒寫
- 推薦使用lft&rgt擅憔,左右值的預排列算法。查詢的性能非常好檐晕。但暑诸,算法的優(yōu)化,沒有固定套路辟灰,得自己寫算法
文章一開始介紹了很多種角色个榕,如果你恰好遇到了,不妨自己推導下芥喇。
3.3 前端菜單
組件的屬性:id/pid/name/接口地址/參數(shù)model等西采,都是前端自己定義的。所以继控,這塊的權限械馆,要讓前端自己配置、維護武通。
- id不要自動生成霹崎。這里的id,特指組件的id冶忱∥补剑可以借助數(shù)據(jù)庫的唯一約束,讓id是唯一的
- 前端的代碼里囚枪,find組件by id派诬。當然,你也可以使用有意義的name眶拉,不過呢千埃,你怎么保證name不會重復呢憔儿?或者忆植,也可以使用組件的路徑(假設組件跟菜單一樣,有嚴格的層級結(jié)構)。無所謂了朝刊,反正是前端自己維護耀里。
-
配置(前端自己配置。后端需要提供配置的表拾氓、邏輯)
組件 --> 角色 --> 用戶
-
權限控制的流程
- 后端需要提供配置的表冯挎、邏輯
- 前端自己配置所有的組件:菜單、按鈕咙鞍、鏈接房官、文件等
- 用戶登錄后,訪問后臺的API续滋,返回配置的組件列表(tree)
- 前端翰守,根據(jù)返回的“組件tree”,動態(tài)展示
- 前端訪問后臺接口的時候疲酌,帶上操作員的token
- 后端判斷“操作員”能否操作資源
3.4 總結(jié)
或許蜡峰,干聊流程,你覺得暈朗恳。來點實在的唄:
相比數(shù)據(jù)庫建模湿颅,我更加推薦DDD:分析業(yè)務流程、推演各個模塊粥诫,最后油航,再考慮配置、存儲
一句話:前端“組件”的權限怀浆,由前端負責劝堪;最嚴格的權限校驗,還是要在后端做揉稚。
4 spring security實戰(zhàn)
源碼
gitHub上的代碼和文檔秒啦,結(jié)合spring security的資料,分步驟實現(xiàn)了這篇文章中的觀點搀玖。
數(shù)據(jù)庫/緩存/服務注冊與發(fā)現(xiàn)/負載均衡等余境,代碼里都沒有。因為灌诅,service mesh已經(jīng)將這些功能都挪到sidecar里了芳来,換句話說,運維自己會搞定的猜拾,開發(fā)人員不需要管