Go中最強(qiáng)大的權(quán)限控制庫(kù)(Casbin)的實(shí)現(xiàn)
權(quán)限控制,它決定了 “誰(shuí)能訪問(wèn)什么資源,能做什么操作”。
Casbin 是一個(gè)強(qiáng)大的、高效的開源訪問(wèn)控制框架,支持 ACL、RBAC、ABAC 等多種經(jīng)典訪問(wèn)控制模型,通過(guò)配置文件即可靈活定義權(quán)限規(guī)則,同時(shí)支持策略的動(dòng)態(tài)管理和持久化存儲(chǔ)。
| 概念 | 作用 | 說(shuō)明 |
|---|---|---|
| Model(模型) | 定義權(quán)限控制的邏輯 | 用 CONF 格式的文件或字符串編寫,定義 “請(qǐng)求是什么、策略是什么、如何匹配、結(jié)果是什么” |
| Policy(策略) | 定義具體的權(quán)限規(guī)則 | 可以存儲(chǔ)在文件、數(shù)據(jù)庫(kù)中,定義 “誰(shuí)(用戶 / 角色)能對(duì)什么資源做什么操作” |
| Adapter(適配器) | 負(fù)責(zé)策略的存儲(chǔ)和加載 | 支持文件、MySQL、PostgreSQL、Redis、MongoDB 等多種存儲(chǔ)方式 |
| Enforcer(執(zhí)行器) | Casbin 的核心組件 | 加載 Model 和 Policy,提供權(quán)限判斷、策略管理等核心 API |
ACL(訪問(wèn)控制列表)
ACL 是最簡(jiǎn)單的訪問(wèn)控制模型,直接定義 “用戶 → 資源 → 操作” 的權(quán)限關(guān)系,適合小型項(xiàng)目或簡(jiǎn)單的權(quán)限場(chǎng)景。
編寫 Model 文件(model.conf)
Model 文件定義了權(quán)限控制的邏輯,Casbin 通過(guò)它來(lái)理解如何判斷權(quán)限。
[request_definition] # 定義請(qǐng)求的格式:r = [主體(sub), 客體(obj), 動(dòng)作(act)] # sub:用戶/角色;obj:資源;act:操作(如 read、write) r = sub, obj, act [policy_definition] # 定義策略的格式:p = [主體(sub), 客體(obj), 動(dòng)作(act)] p = sub, obj, act [policy_effect] # 定義策略效果:e = some(where (p.eft == allow)) # 意思是:只要有一條策略匹配且效果為 allow,就允許訪問(wèn) e = some(where (p.eft == allow)) [matchers] # 定義匹配規(guī)則:m = r.sub == p.sub && r.obj == p.obj && r.act == p.act # 意思是:請(qǐng)求的 sub、obj、act 必須和策略完全一致才匹配 m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
編寫 Policy 文件(policy.csv)
Policy 文件定義了具體的權(quán)限規(guī)則,用 CSV 格式存儲(chǔ)。
# p, 主體, 客體, 動(dòng)作 p, zhangsan, /api/user, read p, zhangsan, /api/user, write p, lisi, /api/article, read
上面的策略表示:
- zhangsan 可以對(duì) /api/user 資源進(jìn)行 read 和 write 操作
- lisi 可以對(duì) /api/article 資源進(jìn)行 read 操作
初始化 Enforcer:加載 Model 文件和 Policy 文件
// 參數(shù) 1:Model 文件路徑
// 參數(shù) 2:Policy 文件路徑
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
fmt.Printf("初始化 Enforcer 失?。?v\n", err)
return
}- casbin.NewEnforcer(modelPath, policyPath) :初始化 Enforcer,加載 Model 和 Policy。
權(quán)限判斷:使用 Enforce 方法
// 參數(shù)順序:sub, obj, act(和 Model 中 r 的定義一致)
// 場(chǎng)景 1:zhangsan 讀取 /api/user
ok, err := e.Enforce("zhangsan", "/api/user", "read")
if err != nil {
fmt.Printf("權(quán)限判斷失?。?v\n", err)
return
}
if ok {
fmt.Println("zhangsan 允許讀取 /api/user")
} else {
fmt.Println("zhangsan 不允許讀取 /api/user")
}
// 場(chǎng)景 2:lisi 寫入 /api/article(策略中沒有這條,應(yīng)該拒絕)
ok, _ = e.Enforce("lisi", "/api/article", "write")
if ok {
fmt.Println("lisi 允許寫入 /api/article")
} else {
fmt.Println("lisi 不允許寫入 /api/article")
}- e.Enforce(sub, obj, act) :核心權(quán)限判斷方法,參數(shù)順序必須和 Model 中 r 的定義一致,返回 bool 表示是否允許訪問(wèn)。
RBAC(基于角色的訪問(wèn)控制)
RBAC 是企業(yè)開發(fā)中最常用的權(quán)限模型,它通過(guò) “角色” 作為中間層,將用戶與權(quán)限解耦(用戶 → 角色 → 權(quán)限),大大簡(jiǎn)化了權(quán)限管理(比如給用戶分配角色即可獲得該角色的所有權(quán)限,無(wú)需逐個(gè)分配)。
編寫 RBAC Model 文件(rbac_model.conf)
RBAC 模型在 ACL 的基礎(chǔ)上增加了角色定義(g) 和 角色繼承。
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] # 定義角色關(guān)系:g = [用戶, 角色] # 表示“用戶屬于某個(gè)角色” g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] # 匹配規(guī)則:g(r.sub, p.sub) 表示“請(qǐng)求的用戶屬于策略中的角色” # 或者請(qǐng)求的用戶直接等于策略中的主體(支持用戶直接分配權(quán)限) m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
編寫 RBAC Policy 文件(rbac_policy.csv)
角色權(quán)限:
- admin 可以對(duì) /api/user 和 /api/article 進(jìn)行 read 和 write
- editor 可以對(duì) /api/article 進(jìn)行 read 和 write
- viewer 只能對(duì) /api/article 進(jìn)行 read
用戶角色:
- zhangsan 是 admin
- lisi 是 editor
- wangwu 是 viewer
# 1. 角色權(quán)限策略:p, 角色, 資源, 動(dòng)作 p, admin, /api/user, read p, admin, /api/user, write p, admin, /api/article, read p, admin, /api/article, write p, editor, /api/article, read p, editor, /api/article, write p, viewer, /api/article, read # 2. 用戶角色策略:g, 用戶, 角色 g, zhangsan, admin g, lisi, editor g, wangwu, viewer
動(dòng)態(tài)管理用戶角色
// 給 wangwu 添加 editor 角色(現(xiàn)在 wangwu 是 viewer + editor)
added, err := e.AddRoleForUser("wangwu", "editor")
if added {
// 保存策略到文件(生產(chǎn)環(huán)境用數(shù)據(jù)庫(kù)適配器自動(dòng)保存)
e.SavePolicy()
}
// 現(xiàn)在 wangwu 應(yīng)該允許寫入 /api/article 了
ok, _ = e.Enforce("wangwu", "/api/article", "write")
// 刪除 wangwu 的 viewer 角色
removed, err := e.DeleteRoleForUser("wangwu", "viewer")
if removed {
e.SavePolicy()
}動(dòng)態(tài)管理權(quán)限
// 給 editor 角色添加 /api/user 的 read 權(quán)限
added, err = e.AddPolicy("editor", "/api/user", "read")
if added {
e.SavePolicy()
}
// 現(xiàn)在 lisi(editor)應(yīng)該允許讀取 /api/user 了
ok, _ = e.Enforce("lisi", "/api/user", "read")| API | 作用 |
|---|---|
| e.AddRoleForUser(user, role) | 給用戶添加角色 |
| e.DeleteRoleForUser(user, role) | 刪除用戶的角色 |
| e.GetRolesForUser(user) | 獲取用戶的所有角色 |
| e.GetUsersForRole(role) | 獲取擁有該角色的所有用戶 |
| e.AddPolicy(sub, obj, act) | 添加權(quán)限策略 |
| e.DeletePolicy(sub, obj, act) | 刪除權(quán)限策略 |
| e.SavePolicy() | 保存策略到存儲(chǔ)(文件 / 數(shù)據(jù)庫(kù)) |
RBAC with Domains(多租戶 / 多部門)
如果你的系統(tǒng)是多租戶(如 SaaS 平臺(tái))或多部門的,需要隔離不同租戶 / 部門的權(quán)限,Casbin 支持 RBAC with Domains 模型,輕松實(shí)現(xiàn)權(quán)限隔離。
在 request_definition 和 matchers 中增加 dom(域 / 租戶)字段:
[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, r.dom, p.sub, p.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
權(quán)限判斷和角色管理時(shí)需要增加 dom 參數(shù):
// 權(quán)限判斷:sub, dom, obj, act
e.Enforce("zhangsan", "tenant1", "/api/user", "read")
// 給用戶添加租戶下的角色
e.AddRoleForUserInDomain("zhangsan", "admin", "tenant1")
數(shù)據(jù)庫(kù)適配器
在開發(fā)環(huán)境中,我們可以用文件存儲(chǔ) Policy,但在生產(chǎn)環(huán)境中,通常需要用數(shù)據(jù)庫(kù)存儲(chǔ) Policy,方便動(dòng)態(tài)管理和持久化。Casbin 提供了豐富的數(shù)據(jù)庫(kù)適配器,這里以最常用的 GORM Adapter(配合 MySQL)為例。
// 1. 連接 MySQL 數(shù)據(jù)庫(kù)
dsn := "root:your_password@tcp(127.0.0.1:3306)/casbin_demo?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// 2. 初始化 GORM Adapter
// 參數(shù) 1:GORM DB 實(shí)例
// 參數(shù) 2:是否自動(dòng)遷移(創(chuàng)建 casbin_rule 表)
a, err := gormadapter.NewAdapterByDB(db)
// 3. 初始化 Enforcer:加載 Model 文件和數(shù)據(jù)庫(kù) Adapter
e, err := casbin.NewEnforcer("./rbac_model.conf", a)
// 4. 初始化策略(僅第一次運(yùn)行時(shí)執(zhí)行,后續(xù)可注釋)
// 添加角色權(quán)限
e.AddPolicy("admin", "/api/user", "read")
e.AddPolicy("admin", "/api/user", "write")
// 添加用戶角色
e.AddRoleForUser("zhangsan", "admin")
// 保存策略到數(shù)據(jù)庫(kù)
e.SavePolicy()
// 5. 權(quán)限判斷
ok, _ := e.Enforce("zhangsan", "/api/user", "write")
// 6. 動(dòng)態(tài)修改策略(自動(dòng)同步到數(shù)據(jù)庫(kù))
e.AddRoleForUser("wangwu", "editor")
ok, _ = e.Enforce("wangwu", "/api/article", "write")- 自動(dòng)同步:使用數(shù)據(jù)庫(kù) Adapter 后,調(diào)用
AddPolicy、AddRoleForUser等 API 時(shí),策略會(huì)自動(dòng)同步到數(shù)據(jù)庫(kù),無(wú)需手動(dòng)調(diào)用SavePolicy()(部分 Adapter 需要,GORM Adapter 自動(dòng)同步)。 - 持久化:策略存儲(chǔ)在數(shù)據(jù)庫(kù)中,服務(wù)重啟后不會(huì)丟失。
- 動(dòng)態(tài)管理:可以通過(guò)數(shù)據(jù)庫(kù)直接管理策略,或通過(guò)后臺(tái)管理界面調(diào)用 Casbin API 管理。
最佳實(shí)踐
Model 設(shè)計(jì)
- Model 文件獨(dú)立存儲(chǔ):將 Model 文件放在項(xiàng)目的
config目錄下,不要硬編碼在代碼里。 - 從簡(jiǎn)單到復(fù)雜:優(yōu)先使用 RBAC,只有在需要屬性級(jí)權(quán)限控制時(shí)才用 ABAC。
- 合理使用通配符:Casbin 支持
*通配符(如p, admin, *, *表示 admin 可以訪問(wèn)所有資源的所有操作),但要謹(jǐn)慎使用,避免權(quán)限過(guò)大。
策略管理
- 生產(chǎn)環(huán)境用數(shù)據(jù)庫(kù) Adapter:不要用文件存儲(chǔ) Policy,推薦用 GORM Adapter 或 Redis Adapter。
- 策略緩存:Casbin 內(nèi)置了策略緩存,高并發(fā)場(chǎng)景下可大幅提升性能,默認(rèn)開啟。
- 定期備份策略:定期備份數(shù)據(jù)庫(kù)中的策略表,防止誤操作導(dǎo)致策略丟失。
性能優(yōu)化
- 使用 EnforceContext:高并發(fā)場(chǎng)景下使用
EnforceContext替代Enforce,支持上下文傳遞和超時(shí)控制。 - 批量權(quán)限判斷:使用
BatchEnforce進(jìn)行批量權(quán)限判斷,性能更高。 - 合理設(shè)計(jì)索引:如果使用數(shù)據(jù)庫(kù) Adapter,確保
casbin_rule表有合理的索引(GORM Adapter 會(huì)自動(dòng)創(chuàng)建)。
安全注意事項(xiàng)
- 不要在策略中存儲(chǔ)敏感信息:策略僅存儲(chǔ)權(quán)限規(guī)則,不要存儲(chǔ)密碼、密鑰等敏感信息。
- 最小權(quán)限原則:給用戶和角色分配最小必要的權(quán)限,避免權(quán)限過(guò)大。
- 權(quán)限驗(yàn)證前置:在 Gin/Echo 等框架的中間件中統(tǒng)一進(jìn)行權(quán)限驗(yàn)證,避免每個(gè)接口都寫權(quán)限判斷代碼。
到此這篇關(guān)于Go中最強(qiáng)大的權(quán)限控制庫(kù)(Casbin)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go Casbin權(quán)限控制庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang優(yōu)雅關(guān)閉channel的方法示例
Goroutine和channel是Go在“并發(fā)”方面兩個(gè)核心feature,下面這篇文章主要給大家介紹了關(guān)于Golang如何優(yōu)雅關(guān)閉channel的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考解決,下面來(lái)一起看看吧。2017-11-11
Go標(biāo)準(zhǔn)庫(kù)之Requests的介紹與基本使用
Python中的Requests庫(kù)非常強(qiáng)大,所以Go開發(fā)者模仿Python的Requests庫(kù),由此誕生了Grequests庫(kù),本文主要介紹了Requests的基本使用,有需要的可以參考下2024-04-04
golang根據(jù)生日計(jì)算星座和屬相實(shí)例
這篇文章主要為大家介紹了golang根據(jù)生日計(jì)算星座和屬相的示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Go語(yǔ)言學(xué)習(xí)函數(shù)+結(jié)構(gòu)體+方法+接口
這篇文章主要介紹了Go語(yǔ)言學(xué)習(xí)函數(shù)+結(jié)構(gòu)體+方法+接口,文章圍繞主題的相關(guān)資料展開詳細(xì)的文章說(shuō)明,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05
Go語(yǔ)言中html/template模塊詳細(xì)功能介紹與示例代碼
這篇文章主要介紹了Go語(yǔ)言中html/template模塊詳細(xì)功能介紹與示例代碼,這里說(shuō)的是go 語(yǔ)言中自帶的包html/template里的一些基本操作,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03

