你:我看完能知道個啥?
我:也就以下兩點吧
一. 了解基於 RBAC 思路的表設計
二. 表數據在實際開發場景中是如何使用的
你:我覺得那應該還有點干貨吧
我:我不要你覺得,我要我覺得 (͡ ͡° ͜ つ ͡͡°)
丹尼爾:Hi,蛋兄,最近接到需求,需要在已有的項目加上權限相關的功能,想想我專心混前端都好久了,N久沒碰表設計了,你對這些有了解嗎?
蛋先生:[]~( ̄▽ ̄)~* 略懂略懂~!已有項目,那就是不能“隨心所欲”咯。說吧,關於已有項目DB的相關信息
丹尼爾:數據庫是用MySQL,連接數據庫用的是Sequelize, 一個ORM的Node.js庫。
蛋先生:OK,這種組合搭配建議的流程是:先用EER圖工具(如MySQLWorkbench
)設計表結構,然后導出SQL,最后通過 Sequelize-Auto
自動生成 Model
丹尼爾:可以啊蛋兄,自動生成SQL,自動生成 Model。好久不見,你還是那么的懶啊 (\^▽^ )。你這么隨便一說,就已經解決了我第一個問題了。那我們接着聊權限設計這塊吧
RBAC表設計
丹尼爾:權限設計,這一塊復雜嗎?
蛋先生:要想多復雜就能多復雜,你想要什么樣的難度系數的?<( ̄ˇ ̄)/
丹尼爾:不不不,我要既簡單又靈活,可以灰常容易擴展那種的 ʅ(´◔౪◔)ʃ
蛋先生:要求挺高的嘛。現在這一塊業界用的較多的是RBAC(Role-based access control)
的思路,即基於角色的存取控制。話不多說,我直接上圖吧
思路非常簡單,就是只需給用戶賦角色,而角色就決定了可以對什么資源(Resource)進行什么樣的操作(Operation),Operation
一般就是CRUD
丹尼爾:users 表為啥沒 password 啊,為啥 code 什么都是 varchar(45) 啊
蛋先生:喂喂,先不要在意這些細節好嗎?ヘ(・_|
丹尼爾:好好好。這表設計看上去挺簡單的,行不行啊?
蛋先生:來,根據你的實際場景,請出招吧
功能權限
丹尼爾:假設有用戶A和用戶B;系統中有項目管理,用戶管理兩個功能;用戶A是管理員,兩個功能都能訪問。而用戶B是普通用戶,只能訪問項目管理,怎么弄?
蛋先生:小意思。┏ (\^ω^)=☞
1. 創建數據
- 創建資源數據:項目管理,用戶管理是屬於功能模塊級別的資源,數據如下:
// resources: { code: 'projects', name: 'projects', type: 'module' }, { code: 'users', name: 'users', type: 'module' },
- 創建角色並賦予相關操作權限
// roles: { code: 'admin', name: 'admin' }, { code: 'guess', name: 'guess' }, // role_permissions: { roleCode: 'admin', resourceCode: 'projects', operation: 'create' }, { roleCode: 'admin', resourceCode: 'projects', operation: 'delete' }, { roleCode: 'admin', resourceCode: 'projects', operation: 'read' }, { roleCode: 'admin', resourceCode: 'projects', operation: 'update' }, { roleCode: 'admin', resourceCode: 'users', operation: 'create' }, { roleCode: 'admin', resourceCode: 'users', operation: 'delete' }, { roleCode: 'admin', resourceCode: 'users', operation: 'read' }, { roleCode: 'admin', resourceCode: 'users', operation: 'update' }, { roleCode: 'guess', resourceCode: 'projects', operation: 'read' },
- 創建用戶並賦予相應角色
// users: { code: 'user_a', name: 'user_a' }, { code: 'user_b', name: 'user_b' }, // user_role: { userCode: 'user_a', roleCode: 'admin' }, { userCode: 'user_b', roleCode: 'guess' },
2. 消費數據
現在我們來給前端童學提供下數據來確定用戶A能看到哪些功能模塊,以及要不要顯示創建,刪除等按鈕
SELECT u.code userCode, res.code resourceCode, GROUP_CONCAT(rp.operation) operations FROM resources res, role_permissions rp, roles r, user_role ur, users u WHERE res.code = rp.resource_code AND rp.role_code = r.code AND r.code = ur.role_code AND ur.user_code = u.code AND res.type = 'module' AND u.code = 'user_a' GROUP BY u.code , res.code
得到的 user_a 的權限如下:
userCode | resourceCode | operations |
---|---|---|
user_a | projects | read,delete,update,create |
user_a | users | delete,update,create,read |
這樣,前端只需判斷 projects 是否擁有 read 的 operation,即可決定是否顯示項目功能菜單。如果有 create,則顯示創建按鈕;有 delete,則顯示刪除按鈕;有 update,則顯示編輯按鈕
3. 視圖簡化
丹尼爾:問題是解了,但那SQL,是不是有點復雜啊 (~ ̄▽ ̄)~
蛋先生:額,確實。那就來簡化一下吧。
通過以下SQL創建用戶功能模塊權限的視圖view
CREATE VIEW `user_module_view` AS SELECT u.code user_code, rp.resource_code, rp.operation, rp.op_modifier FROM role_permissions rp, users u, user_role ur, resources rs WHERE ur.role_code = rp.role_code AND u.code = ur.user_code AND rs.code = rp.resource_code AND rs.type = 'module'
現在我們就可以把剛剛上面冗長的SQL簡化成以下的單表操作了:
SELECT user_code, resource_code, GROUP_CONCAT(operation) operation FROM user_module_view WHERE user_code = 'user_a' GROUP BY user_code , resource_code
數據權限
丹尼爾:那我繼續出題咯。用戶A和用戶B雖然都對項目管理功能有 read 權限,但用戶B是普通用戶,假設用戶B屬於OrgB組織,那他就只能查看OrgB下的項目時該昨弄?
蛋先生:還記得 role_permission
的 op_modifier
字段嗎,這就是用來修飾 operation
的。現在我們修改下 role_permission
的數據
{ roleCode: 'guess', resourceCode: 'projects', operation: 'read' } => { roleCode: 'guess', resourceCode: 'projects', operation: 'read', op_modifier: 'org' },
這表示 guess 角色對 projects 資源擁有 org 范圍的 read 權限。這樣當服務端接口在取項目列表數據時,可以根據 op_modifier
的值來決定列表數據的過濾條件
丹尼爾:常規的需求好像都沒什么問題。不過我現在這邊有個權限相關的需求,不知道你這套能不能派上用場
蛋先生:來吧,我今天就奉陪到底了 ( ̄︶ ̄)↗
丹尼爾:那我就不客氣了。我的項目管理功能中,每個項目創建后都默認有 view / edit / admin 角色。上面的例子只能對指定范圍(比如org)的項目作相同的操作,但不同項目指定不同的操作,好像實現不了
蛋先生:[]~( ̄▽ ̄)~* 那就換個角度唄,把每一個項目都當作資源怎么樣。
丹尼爾:能說得具體一些嗎?最好能說下創建項目的時候權限這塊該做些什么
蛋先生:咳咳咳~,沒問題,來咯
按你的要求,在創建項目時,就需要初始化相應的內置角色,這樣才能給用戶分配角色。下面就說下假設創建項目project_a,需要給哪些表增加哪些數據
// 1. add resource: { code: 'project_a', name: 'project_a', type: 'project' } // 2. add roles: { code: 'pro_a_view', name: 'pro_a_view' }, { code: 'pro_a_edit', name: 'pro_a_edit' }, { code: 'pro_a_admin', name: 'pro_a_admin' }, // 3. add role_permission: { roleCode: 'pro_a_view', resourceCode: 'project_a', operation: 'read' }, { roleCode: 'pro_a_edit', resourceCode: 'project_a', operation: 'read' }, { roleCode: 'pro_a_edit', resourceCode: 'project_a', operation: 'update' }, { roleCode: 'pro_a_admin', resourceCode: 'project_a', operation: 'read' }, { roleCode: 'pro_a_admin', resourceCode: 'project_a', operation: 'update' }, { roleCode: 'pro_a_admin', resourceCode: 'project_a', operation: 'delete' },
這樣只需要給用戶B增加pro_a_view
角色,用戶B即擁有對 project_a 的讀權限
注意這里operation
並沒有create
,因為資源是指單個項目,所以單個項目哪來的create呢?是吧 (\^▽^ )
丹尼爾:恩,看上去跟整個項目功能作為資源的時候是一個樣的。但我發現個問題,如果以每個項目作為資源,那我要查詢用戶B能看到哪些項目,好像很麻煩啊。總不能一個一個找,然后合在一起吧
蛋先生:當然,還記得上面我們用過視圖view
嗎?現在我們也給 project 類型的資源創建個view吧
CREATE VIEW 'user_project_view' AS SELECT u.code user_code, rp.resource_code, rp.operation, rp.op_modifier FROM role_permissions rp, users u, user_role ur, resources rs WHERE ur.role_code = rp.role_code AND u.code = ur.user_code AND rs.code = rp.resource_code AND rs.type = 'project'
這樣同樣只需單表就能查詢用戶B能查看的項目列表以及每個項目的操作權限了
SELECT user_code, resource_code, GROUP_CONCAT(operation) FROM user_project_view WHERE user_code = 'user_b' GROUP BY user_code , resource_code HAVING GROUP_CONCAT(operation) LIKE '%read%'
丹尼爾:哎呦不錯。我還有最后一個需求,就是項目中的圖片資源,如果用戶B對 project_a 擁有 edit 角色,則只能刪除自己添加的圖片資源,不能刪除其他人添加的圖片資源,這個能實現嗎。圖片資源我可不想再像項目一樣作為資源記錄哦
蛋先生:(lll¬ω¬) 這個嘛...
丹尼爾:看來難倒你了,哈哈
蛋先生:非也非也。強大的op_modifier
可不是吃素的。我只需對edit角色的update操作權限增加limited
的修飾符即可
丹尼爾:這都行,好像有道理哦。由於op_modifier
可以擴展,所以只要我們規定了它的行為,好像什么都可以搞定一樣
蛋先生:All right。擴展性是一定要具備的,而op_modifier
就是擴展的關鍵所在。op_modifier
定義了操作的修飾符,開發者根據修飾符的約定,實現指定邏輯即可
丹尼爾:明白了,謝了,蛋兄,告辭告辭
蛋先生:客氣客氣,走好不送!
END
為了更高的交流,歡迎大家關注我的公眾號,掃描下面二維碼即可關注,謝謝: