PHP-Casbin
基礎(chǔ)知識
概述
Casbin是一個強大的覆山、高效的開源訪問控制框架,其權(quán)限管理機制支持多種訪問控制模型除抛。
Casbin是什么丧失?
Casbin可以做到:
- 支持自定義請求的格式,默認的請求格式為
{subject, object, action}
砂代。 - 具有訪問控制模型model和策略policy兩個核心概念蹋订。
- 支持RBAC中的多層角色繼承,不止主體可以有角色刻伊,資源也可以具有角色辅辩。
- 支持超級用戶,如
root
或Administrator
娃圆,超級用戶可以不受授權(quán)策略的約束訪問任意資源。 - 支持多種內(nèi)置的操作符蛾茉,如
keyMatch
讼呢,方便對路徑式的資源進行管理,如/foo/bar
可以映射到/foo*
Casbin不能做到:
- 身份認證 authentication(即驗證用戶的用戶名谦炬、密碼)悦屏,casbin只負責(zé)訪問控制。應(yīng)該有其他專門的組件負責(zé)身份認證键思,然后由casbin進行訪問控制础爬,二者是相互配合的關(guān)系。
- 管理用戶列表或角色列表吼鳞。 Casbin 認為由項目自身來管理用戶看蚜、角色列表更為合適, 用戶通常有他們的密碼赔桌,但是 Casbin 的設(shè)計思想并不是把它作為一個存儲密碼的容器供炎。 而是存儲RBAC方案中用戶和角色之間的映射關(guān)系。
安裝
composer require casbin/casbin
開始使用
require_once './vendor/autoload.php';
use Casbin\Enforcer;
$e = new Enforcer("path/to/model.conf", "path/to/policy.csv");
你可以在初始化Enforcer實例時疾党,使用數(shù)據(jù)庫代替文件音诫,相關(guān)說明,后面將會提到
進行訪問控制
$sub = "alice"; // 角色名
$obj = "data1"; // 訪問的資源
$act = "read"; // 訪問的權(quán)限
// 驗證該角色是否有訪問某資源的權(quán)限
if ($e->enforce($sub, $obj, $act) === true) {
// 允許訪問
} else {
// 禁止訪問
}
Casbin也提供了API用于權(quán)限管理雪位,例如:你可以獲取分配給某個角色的所有權(quán)限
$roles = $e->getRolesForUser("alice");
查看 Management API 和 RBAC API 獲取更多的用法
請參考測試用例來了解更多的用法
工作原理
在 Casbin 中, 訪問控制模型被抽象為基于 PERM (Policy, Effect, Request, Matcher) 的一個文件竭钝。 因此,切換或升級項目的授權(quán)機制與修改配置一樣簡單雹洗。 您可以通過組合可用的模型來定制您自己的訪問控制模型香罐。 例如,您可以在一個model中獲得RBAC角色和ABAC屬性时肿,并共享一組policy規(guī)則穴吹。
Policy:策略 Effect:作用范圍 Request:請求 Matcher:匹配器
Casbin中最基本、最簡單的model是ACL嗜侮。ACL中的model CONF為:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
ACL model的示例policy如下:
p, alice, data1, read
p, bob, data2, write
這表示:
- alice可以讀取data1
- bob可以編寫data2
對于過長的單行配置港令,您也可以通過在結(jié)尾處添加“\”進行斷行:
# 匹配器
[matchers]
m = r.sub == p.sub && r.obj == p.obj \
&& r.act == p.act
此外啥容,對于 ABAC,您在可以在 Casbin golang 版本中嘗試下面的 (jCasbin 和 Node-Casbin 尚不支持)操作:
# 匹配器
[matchers]
m = r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3')
但是你應(yīng)確保數(shù)組的長度大于 1顷霹,否則的話將會導(dǎo)致 panic 咪惠。
對于更多操作,你可以查看govaluate淋淀。
Model
支持的Models
- ACL (Access Control List, 訪問控制列表)
- 具有的超級用戶ACL
- 沒有用戶的 ACL: 對于沒有身份驗證或用戶登錄的系統(tǒng)尤其有用遥昧。
-
沒有資源的 ACL: 某些場景可能只針對資源的類型, 而不是單個資源, 諸如
write-article
,read-log
等權(quán)限。 它不控制對特定文章或日志的訪問朵纷。 - RBAC (基于角色的訪問控制)
- 支持資源角色的RBAC: 用戶和資源可以同時具有角色 (或組)炭臭。
- 支持域/租戶的RBAC: 用戶可以為不同的域/租戶設(shè)置不同的角色集。
-
ABAC (基于屬性的訪問控制): 支持利用
resource.Owner
這種語法糖獲取元素的屬性袍辞。 - RESTful: 支持路徑, 如 /res/*, /res/: id 和 HTTP 方法, 如 GET, POST, PUT, DELETE鞋仍。
- 拒絕優(yōu)先: 支持允許和拒絕授權(quán), 拒絕優(yōu)先于允許。
- 優(yōu)先級: 策略規(guī)則按照先后次序確定優(yōu)先級搅吁,類似于防火墻規(guī)則威创。
例子
訪問控制模型 | Model 文件 | Policy 文件 |
---|---|---|
ACL | basic_model.conf | basic_policy.csv |
具有超級用戶的ACL | basic_model_with_root.conf | basic_policy.csv |
沒有用戶的ACL | basic_model_without_users.conf | basic_policy_without_users.csv |
沒有資源的ACL | basic_model_without_resources.conf | basic_policy_without_resources.csv |
RBAC | rbac_model.conf | rbac_policy.csv |
支持資源角色的RBAC | rbac_model_with_resource_roles.conf | rbac_policy_with_resource_roles.csv |
支持域/租戶的RBAC | rbac_model_with_domains.conf | rbac_policy_with_domains.csv |
ABAC | abac_model.conf | 無 |
RESTful | keymatch_model.conf | keymatch_policy.csv |
拒絕優(yōu)先 | rbac_model_with_deny.conf | rbac_policy_with_deny.csv |
優(yōu)先級 | priority_model.conf | priority_policy.csv |
Model語法
- Model CONF 至少應(yīng)包含四個部分:
[request_definition], [policy_definition], [policy_effect], [matchers]
。 - 如果 model 使用 RBAC, 還需要添加
[role_definition]
部分谎懦。 - 一個Model CONF可以包含注釋肚豺。注釋以#開頭
request_definition:請求定義 policy_definition:策略定義 policy_effect:策略作用范圍 matchers:匹配器 role_definition:角色定義
Request定義
[request_definition]
部分用于request的定義,它明確了 $e->enforce($sub, $obj, $act)
函數(shù)中參數(shù)的含義界拦。
# 請求定義
[request_definition]
r = sub, obj, act
sub, obj, act
表示經(jīng)典三元組: 訪問實體 (Subject)吸申,訪問資源 (Object) 和訪問方法 (Action)。 但是, 你可以自定義你自己的請求表單, 如果不需要指定特定資源享甸,則可以這樣定義 sub呛谜、act
,或者如果有兩個訪問實體, 則為 sub枪萄、sub2隐岛、obj、act
瓷翻。
Policy定義
[policy_definition]
部分是對policy的定義聚凹,以下文的 model 配置為例:
# 策略定義
[policy_definition]
p = sub, obj, act
p2 = sub, act
這些是我們對policy規(guī)則的具體描述
p, alice, data1, read
p2, bob, write-all-objects
policy部分的每一行稱之為一個策略規(guī)則, 每條策略規(guī)則通常以形如p
, p2
的policy type
開頭齐帚。 如果存在多個policy定義响禽,那么我們會根據(jù)前文提到的policy type
與具體的某條定義匹配偿洁。 上面的policy的綁定關(guān)系將會在matcher中使用顷牌, 羅列如下:
(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
注1: 當前只支持形如 p
的單個policy定義己单, 形如p2
類型的尚未支持。 通常情況下, 用戶無需使用多個 policy 定義剪菱, 如果您有其他情形的policy定義訴求摩瞎,請在 https://github.com/casbin/casbin/issues/new 提出issue告知我們拴签。
注2: policy定義中的元素始終被視為字符串(string
)對待, 如果您對此有疑問旗们,請移步https://github.com/casbin/casbin/issues/113
Policy effect定義
[policy_effect]
部分是對policy生效范圍的定義蚓哩, 原語定義了當多個policy rule同時匹配訪問請求request時,該如何對多個決策結(jié)果進行集成以實現(xiàn)統(tǒng)一決策。 以下示例展示了一個只有一條規(guī)則生效上渴,其余都被拒絕的情況:
# 策略生效范圍
[policy_effect]
e = some(where (p.eft == allow))
該Effect原語表示如果存在任意一個決策結(jié)果為allow
的匹配規(guī)則岸梨,則最終決策結(jié)果為allow
,即allow-override稠氮。 其中p.eft
表示策略規(guī)則的決策結(jié)果曹阔,可以為allow
或者deny
,當不指定規(guī)則的決策結(jié)果時,取默認值allow
隔披。 通常情況下赃份,policy的p.eft
默認為allow
, 因此前面例子中都使用了這個默認值锹锰。
這是另一個policy effect的例子:
# 策略生效范圍
[policy_effect]
e = !some(where (p.eft == deny))
該Effect原語表示不存在任何決策結(jié)果為deny
的匹配規(guī)則,則最終決策結(jié)果為allow
漓库,即deny-override恃慧。 some
量詞判斷是否存在一條策略規(guī)則滿足匹配器。 any
量詞則判斷是否所有的策略規(guī)則都滿足匹配器 (此處未使用)渺蒿。
policy effect還可以利用邏輯運算符進行連接:
# 策略生效范圍
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
該Effect原語表示當至少存在一個決策結(jié)果為allow
的匹配規(guī)則痢士,且不存在決策結(jié)果為deny
的匹配規(guī)則時,則最終決策結(jié)果為allow
茂装。 這時allow
授權(quán)和deny
授權(quán)同時存在怠蹂,但是deny
優(yōu)先。
Matchers
[matchers]
原語定義了策略規(guī)則如何與訪問請求進行匹配的匹配器少态,其本質(zhì)上是布爾表達式城侧,可以理解為Request、Policy等原語定義了關(guān)于策略和請求的變量,然后將這些變量代入Matcher原語中求值,從而進行策略決策彼妻。
# 匹配器
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
這是一個簡單的例子嫌佑,該Matcher原語表示,訪問請求request中的subject、object侨歉、action三元組應(yīng)與策略規(guī)則policy rule中的subject屋摇、object、action三元組分別對應(yīng)相同幽邓。
Matcher原語支持+炮温、 -、 *牵舵、 /等算數(shù)運算符柒啤,==,倦挂、!=、 >白修、 <等關(guān)系運算符以及&& (與)妒峦、|| (或)、 ! (非)等邏輯運算符兵睛。
注: 雖然可以像其他原語一樣的編寫多個類似于 m1
, m2
的matcher肯骇, 但是當前我們只支持一個有效的 matcher m
。 通常情況下祖很,您可以在一個matcher中使用上文提到的邏輯運算符來實現(xiàn)復(fù)雜的邏輯判斷笛丙, 因而我們認為目前不需要支持多個matcher。 如果您對此有疑問假颇,請告知我們(https://github.com/casbin/casbin/issues)胚鸯。
matcher中的函數(shù)
matcher的強大與靈活之處在于您甚至可以在matcher中定義函數(shù),這些函數(shù)可以是內(nèi)置函數(shù)或自定義的函數(shù)笨鸡。當前支持的內(nèi)置函數(shù)如下:
函數(shù) | 釋義 | 示例 |
---|---|---|
keyMatch(arg1, arg2) | 參數(shù) arg1 是一個 URL 路徑姜钳,例如 /alice_data/resource1 ,參數(shù) arg2 可以是URL路徑或者是一個 * 模式形耗,例如 /alice_data/* 哥桥。此函數(shù)返回 arg1是否與 arg2 匹配。 |
keymatch_model.conf/keymatch_policy.csv |
keyMatch2(arg1, arg2) | 參數(shù) arg1 是一個 URL 路徑激涤,例如 /alice_data/resource1 拟糕,參數(shù) arg2 可以是 URL 路徑或者是一個 : 模式,例如/alice_data/:resource 倦踢。此函數(shù)返回 arg1 是否與 arg2 匹配送滞。 |
keymatch2_model.conf/keymatch2_policy.csv |
regexMatch(arg1, arg2) | arg1 可以是任何字符串。arg2 是一個正則表達式辱挥。它返回 arg1 是否匹配 arg2犁嗅。 | keymatch_model.conf/keymatch_policy.csv |
ipMatch(arg1, arg2) | arg1 是一個 IP 地址, 如 192.168.2.123 。arg2 可以是 IP 地址或 CIDR, 如 192.168.2. 0/24 晤碘。它返回 arg1 是否匹配 arg2愧哟。 |
ipmatch_model.conf/ipmatch_policy.csv |
如何添加自定義函數(shù)(GO語言示例)
首先準備好一個有幾個參數(shù)和一個布爾值返回值的函數(shù):
func KeyMatch(key1 string, key2 string) bool {
i := strings.Index(key2, "*")
if i == -1 {
return key1 == key2
}
if len(key1) > i {
return key1[:i] == key2[:i]
}
return key1 == key2[:i]
}
然后用 interface{}
類型包裝此函數(shù):
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(KeyMatch(name1, name2)), nil
}
最后, 將該函數(shù)注冊到 Casbin enforcer:
e.AddFunction("my_func", KeyMatchFunc)
現(xiàn)在, 您可以像下面這樣使用 model 中的函數(shù):
# 匹配器
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act
RBAC
角色定義
[role_definition]
是RBAC角色繼承關(guān)系的定義。 Casbin 支持 RBAC 系統(tǒng)的多個實例, 例如, 用戶可以具有角色及其繼承關(guān)系, 資源也可以具有角色及其繼承關(guān)系哼蛆。 這兩個 RBAC 系統(tǒng)不會互相干擾蕊梧。
此部分是可選的。 如果在模型中不使用 RBAC 角色, 則省略此部分腮介。
# 角色定義
[role_definition]
g = _, _
g2 = _, _
上述角色定義表明, g
是一個 RBAC系統(tǒng), g2
是另一個 RBAC 系統(tǒng)肥矢。 _, _
表示角色繼承關(guān)系的前項和后項,即前項繼承后項角色的權(quán)限。 一般來講甘改,如果您需要進行角色和用戶的綁定旅东,直接使用g
即可。 當您需要表示角色(或者組)與用戶和資源的綁定關(guān)系時十艾,可以使用g
和 g2
這樣的表現(xiàn)形式抵代。 請參見 rbac_model 和 rbac_model_with_resource_roles 的示例。
在Casbin里忘嫉,我們以policy表示中實際的用戶角色映射關(guān)系 (或是資源-角色映射關(guān)系)荤牍,例如:
p, data2_admin, data2, read
g, alice, data2_admin
這意味著 alice
是角色 data2_admin
的一個成員。 alice
在這里可以是用戶庆冕、資源或角色康吵。 Cabin 只是將其識別為一個字符串。
接下來在matcher中访递,應(yīng)該像下面的例子一樣檢查角色信息:
# 匹配器
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
它表示請求中的sub
應(yīng)該在策略中定義了sub
角色晦嵌。
有幾個注意事項:
- Casbin 只存儲用戶角色的映射關(guān)系。
- Cabin 沒有驗證用戶是否是有效的用戶拷姿,或者角色是一個有效的角色惭载。 這應(yīng)該通過認證來解決。
- RBAC 系統(tǒng)中的用戶名稱和角色名稱不應(yīng)相同响巢。因為Casbin將用戶名和角色識別為字符串描滔, 所以當前語境下Casbin無法得出這個字面量到底指代用戶
alice
還是角色alice
。 這時抵乓,使用明確的role_alice
伴挚,問題便可迎刃而解靶衍。 - 假設(shè)
A
具有角色B
灾炭,B
具有角色C
,并且A
有角色C
颅眶。 這種傳遞性在當前版本會造成死循環(huán)蜈出。
角色層次
Casbin 的 RBAC 支持 RBAC1 的角色層次結(jié)構(gòu)功能,如果 alice
具有role1
, role1
具有role2
涛酗,則 alice
也將擁有 role2
并繼承其權(quán)限铡原。
下面是一個稱為層次結(jié)構(gòu)級別的概念。 因此, 此示例的層次結(jié)構(gòu)級別為2商叹。 對于Casbin中的內(nèi)置角色管理器, 可以指定最大層次結(jié)構(gòu)級別燕刻。 默認值為10。 這意味著終端用戶 alice
只能繼承10個級別的角色剖笙。
// GO語言示例
// NewRoleManager重寫了默認角色管理RoleManger的構(gòu)造方法
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
rm := RoleManager{}
rm.allRoles = &sync.Map{}
rm.maxHierarchyLevel = maxHierarchyLevel
rm.hasPattern = false
return &rm
}
如何區(qū)分用戶和角色卵洗?
在RBAC中,Casbin不對用戶和角色進行區(qū)分弥咪。 它們都被視為字符串过蹂。 如果你只使用單層的RBAC模型(角色不會成為另一個角色的成員)十绑。 可以使用 e.GetAllSubjects()
獲取所有用戶,e.GetAllRoles()
獲取所有角色酷勺。 它們會為規(guī)則 g, u, r
分別列出所有的 u
和 r
本橙。
但在使用多層RBAC模型時(帶有角色繼承),你的應(yīng)用不會記錄一個名字(字符串)是用戶還是角色脆诉,或者用戶和角色有相同的名字甚亭。 可以給角色加上像 role::admin
的前綴再傳遞到Casbin中。 由此可以通過查看前綴來區(qū)分用戶和角色库说。
如何查詢隱性角色或權(quán)限狂鞋?
當用戶通過RBAC層次結(jié)構(gòu)繼承角色或權(quán)限,而不是直接在策略規(guī)則中分配它們時潜的,我們將這種類型的分配稱為 implicit
骚揍。 要查詢這種隱式關(guān)系,需要使用以下兩個api: GetImplicitRolesForUser()
以及 GetImplicitPermissionsForUser
替代GetRolesForUser()
以及 GetPermissionsForUser
. 有關(guān)詳情啰挪,請參閱 this GitHub issue信不。
在 RBAC 中使用模式匹配
有時,您希望將具有特定模式的某些subjects(或objects)自動授予某個角色亡呵。 RBAC中的模式匹配函數(shù)可以幫助做到這一點抽活。 模式匹配函數(shù)與前一個函數(shù)共享相同的參數(shù)和返回值:matcher function。
我們知道RBAC在matcher中通常表示為g(r.sub, p.sub)
锰什。 然后我們將使用以下策略:
p, alice, book_group, read
g, /book/1, book_group
g, /book/2, book_group
所以 alice
可以讀所有的book下硕,包括 book 1
和book 2
。 但是可能有成千上萬個book汁胆,使用 g
策略規(guī)則將每一個book添加到book角色(或組)是非常單調(diào)乏味的梭姓。
但是使用模式匹配函數(shù),您可以只用一行代碼編寫策略:
g, /book/:id, book_group
Casbin會自動將 /book/1
和/book/2
匹配成模式/book/:id
嫩码。 您只需要向強制程序注冊該功能誉尖,如:
// GO語言示例
e.rm.(*defaultrolemanager.RoleManager).AddMatchingFunc("KeyMatch2", util.KeyMatch2)
您可以在這里看到完整的示例 :here。
值得注意的是:
- 只有
g
種的第一參數(shù) (aka 用戶) 支持模式函數(shù)铸题。 您正在使用第三個參數(shù)(domain)铡恕,目前不支持。
角色管理器
角色管理器用于管理Casbin中的RBAC角色層次結(jié)構(gòu)(用戶角色映射)丢间。 角色管理器可以從Casbin策略規(guī)則或外部源(如LDAP探熔、Okta、Auth0烘挫、Azure AD等)檢索角色數(shù)據(jù)诀艰。 我們支持角色管理器的不同實現(xiàn)。 為了保持代碼輕量級,我們沒有把角色管理器代碼放在主庫中(默認的角色管理器除外)涡驮。 下面提供了Casbin角色管理器的完整列表暗甥。 歡迎任何第三方對角色manager進行新的貢獻,如果有請告知我們捉捅,我們將把它放在這個列表中:
角色管理器 | 作者 | 描述 |
---|---|---|
Default Role Manager (built-in) | Casbin | 支持存儲在Casbin策略中的角色層次結(jié)構(gòu) |
Session Role Manager | EDOMO Systems | 支持存儲在Casbin策略中的角色層次結(jié)構(gòu)撤防,以及基于時間范圍的會話 |
Okta Role Manager | Casbin | 支持存儲在Okta中的角色層次結(jié)構(gòu) |
Auth0 Role Manager | Casbin | 支持存儲在 Auth0's Authorization Extension 授權(quán)擴展名中的角色層次結(jié)構(gòu) |
對于開發(fā)人員:所有角色manager必須實現(xiàn) RoleManager接口。 Session Role Manager可以用作參考實現(xiàn)棒口。
RBAC with Domains
域租戶的角色定義
在Casbin中的RBAC角色可以是全局或是基于特定于域的寄月。 特定域的角色意味著當用戶處于不同的域/租戶群體時,用戶所表現(xiàn)的角色也不盡相同无牵。 這對于像云服務(wù)這樣的大型系統(tǒng)非常有用漾肮,因為用戶通常分屬于不同的租戶群體。
域/租戶的角色定義應(yīng)該類似于:
# 角色定義
[role_definition]
g = _, _, _
第三個 _
表示域/租戶的名稱, 此部分不應(yīng)更改茎毁。
然后克懊,策略可以是:
p, admin, tenant1, data1, read
p, admin, tenant2, data2, read
g, alice, admin, tenant1
g, alice, user, tenant2
該實例表示tenant1
的域內(nèi)角色admin
可以讀取data1
, alice
在tenant1
域中具有admin
角色七蜘,但在tenant2
域中具有user
角色橡卤, 所以alice可以有讀取data1
的權(quán)限嵌灰。 同理秕脓,因為alice
不是tenant2
的admin
搂鲫,所以她訪問不了data2
拣挪。
接下來在matcher中,應(yīng)該像下面的例子一樣檢查角色信息:
# 匹配器
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
更多示例參見: rbac_with_domains_model.conf园骆。
ABAC
什么是ABAC模式巍耗?
ABAC是 基于屬性的訪問控制
亲族,可以使用主體可缚、客體或動作的屬性帘靡,而不是字符串本身來控制訪問。 您之前可能就已經(jīng)聽過 XACML 描姚,是一個復(fù)雜的 ABAC 訪問控制語言筒扒。 與XACML相比冰蘑,Casbin的ABAC非常簡單: 在ABAC中祠肥,可以使用struct(或基于編程語言的類實例) 而不是字符串來表示模型元素允跑。
例如,ABAC的官方實例如下:
# 請求定義
[request_definition]
r = sub, obj, act
# 政策定義
[policy_definition]
p = sub, obj, act
# 政策應(yīng)用范圍
[policy_effect]
e = some(where (p.eft == allow))
# 匹配器
[matchers]
m = r.sub == r.obj.Owner
我們使用 r.obj.所有者
代替 r.obj
matcher。 在 Enforce()
函數(shù)中傳遞的 r.obj
函數(shù)是結(jié)構(gòu)或類實例,而不是字符串棉姐。 Casbin將使用映像來檢索 obj
結(jié)構(gòu)或類中的成員變量。
這里是 r.obj
construction 或 class 的定義:
// GO語言示例
type testResource struct {
Name string
Owner string
}
如何使用ABAC弱睦?
簡單地說,要使用ABAC渊额,您需要做兩件事:
- 在模型匹配器中指定屬性况木。
- 將元素的結(jié)構(gòu)或類實例作為Casbin的
Enforce()
的參數(shù)傳入。
Note:
- 目前旬迹,僅請求元素火惊,例如
r.such
、[r.obj
奔垦,]r.action
等等支持ABAC的元素屹耐。 您不能在策略元素上使用它,比如p.sub
椿猎,因為在Casbin的策略中沒有定義結(jié)構(gòu)或者類惶岭。 - 您可以在一個matcher中使用多個ABAC屬性,例如:
m = r.sub.Domain == r.obj.Domain
犯眠。
存儲
Model存儲
與 policy 不同按灶,model 只能加載,不能保存筐咧。 因為我們認為 model 不是動態(tài)組件鸯旁,不應(yīng)該在運行時進行修改,所以我們沒有實現(xiàn)一個 API 來將 model 保存到存儲中量蕊。
但是铺罢,好消息是,我們提供了三種等效的方法來靜態(tài)或動態(tài)地加載模型:
從 .CONF 文件中加載 model
當你向 Casbin 團隊尋求幫助時危融,他們會給你這個 Casbin 最常用的方法畏铆,此方法對于初學(xué)者來說很容易理解并且便于分享雷袋。
.CONF
文件的內(nèi)容 examples/rbac_model.conf:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
接著你可以加載模型文件如下:
// GO語言示例
e := casbin.NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
從代碼加載 model
型可以從代碼中動態(tài)初始化吉殃,不需要使用 .CONF
辞居。下面是RBAC模型的一個例子:
// GO語言示例
// 初始化model
m := model.NewModel()
m.AddDef("r", "r", "sub, obj, act")
m.AddDef("p", "p", "sub, obj, act")
m.AddDef("g", "g", "_, _")
m.AddDef("e", "e", "some(where (p.eft == allow))")
m.AddDef("m", "m", "g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act")
// 使用自己的 adapter 替換。
a := persist.NewFileAdapter("examples/rbac_policy.csv")
// 創(chuàng)建一個 enforcer蛋勺。
e := casbin.NewEnforcer(m, a)
從字符串加載的 model
或者您可以從多行字符串加載整個模型文本瓦灶。這種方法的優(yōu)點是您不需要維護模型文件。
// GO語言示例
// Initialize the model from a string.
text :=
`
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
`
m := NewModel(text)
// Load the policy rules from the .CSV file adapter.
// Replace it with your adapter to avoid files.
a := persist.NewFileAdapter("examples/rbac_policy.csv")
// Create the enforcer.
e := casbin.NewEnforcer(m, a)
Policy存儲
在Casbin中抱完,策略存儲作為adapter實現(xiàn)贼陶。請參照:</docs/en/adapters>。
Policy Subset Loading
一些adapter支持過濾策略管理巧娱。 這意味著Casbin加載的策略是基于給定過濾器的存儲策略的子集碉怔。 當解析整個策略成為性能瓶頸時,這將會允許在大型多租戶環(huán)境中有效地執(zhí)行策略禁添。
要使用支持的adapter處理過濾后的策略撮胧,只需調(diào)用 LoadFilteredPolicy
方法。 過濾器參數(shù)的有效格式取決于所用的適配器老翘。 為了防止意外數(shù)據(jù)丟失芹啥,當策略已經(jīng)加載, SavePolicy
方法會被禁用铺峭。
例如墓怀,下面的代碼片段使用內(nèi)置的過濾文件adapter和帶有域的RBAC模型。 在本例中卫键,過濾器將策略限制為單個域傀履。 除 "domain1"
以外的任何域策略行被忽略:
// GO語言示例
import "github.com/casbin/casbin"
enforcer := casbin.NewEnforcer()
adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv")
enforcer.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter)
filter := &fileadapter.Filter{
P: []string{"", "domain1"},
G: []string{"", "", "domain1"},
}
enforcer.LoadFilteredPolicy(filter)
// The loaded policy now only contains the entries pertaining to "domain1".
Extensions
Adapters
在Casbin中,策略存儲作為adapter(Casbin的中間件) 實現(xiàn)莉炉。 Casbin用戶可以使用adapte從存儲中加載策略規(guī)則 (aka LoadPolicy()
) 或者將策略規(guī)則保存到其中 (aka SavePolicy()
)啤呼。 為了保持代碼輕量級,我們沒有把adapte代碼放在主庫中呢袱。
目前支持的adapter列表
Casbin角色管理器的完整列表如下所示官扣。 歡迎任何第三方對adapter進行新的貢獻,如果有請通知我們羞福,我們將把它放在這個列表中:)
適配器 | 類型 | 作者 | 自動保存 | 描述 |
---|---|---|---|---|
File Adapter (built-in) | File | Casbin | ? | For .CSV (Comma-Separated Values) files |
Database Adapter | ORM | Casbin | ? | MySQL, PostgreSQL, SQLite, Microsoft SQL Server are supported by techone/database |
Zend Db Adapter | ORM | Casbin | ? | MySQL, PostgreSQL, SQLite, Oracle, IBM DB2, Microsoft SQL Server, Other PDO Driver are supported by zend-db |
Doctrine DBAL Adapter(Recommend) | ORM | Casbin | ? | Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management. |
Medoo Adapter | ORM | Casbin | ? |
Medoo is a lightweight PHP Database Framework to Accelerate Development, supports all SQL databases, including MySQL , MSSQL , SQLite , MariaDB , PostgreSQL , Sybase , Oracle and more. |
這里有一些你需要知道的事情:
- 如果使用顯式或隱式adapter調(diào)用
casbin.NewEnforcer()
惕蹄,策略將自動加載。 - 可以調(diào)用
e.LoadPolicy()
來從存儲中重新加載策略規(guī)則治专。 - 如果adapter不支持
Auto-Save
特性卖陵,則在添加或刪除策略時不能將策略規(guī)則自動保存回存儲器。 您可以手動調(diào)用SavePolicy()
來保存所有策略規(guī)則张峰。
例子
這里我門提供了幾個例子:
文件適配器 (內(nèi)置)
下面展示了如何對內(nèi)置的文件適配器進行初始化:
use Casbin\Enforcer;
$e = new Enforcer('examples/basic_model.conf', 'examples/basic_policy.csv');
同樣的:
use Casbin\Enforcer;
use Casbin\Persist\Adapters\FileAdapter;
$a = new FileAdapter('examples/basic_policy.csv');
$e = new Enforcer('examples/basic_model.conf', $a);
MySQL 適配器
下面展示了如何初始化一個MySQL適配器
// https://github.com/php-casbin/dbal-adapter
use Casbin\Enforcer;
use CasbinAdapter\DBAL\Adapter as DatabaseAdapter;
$config = [
// driver的可選值:
// pdo_mysql,pdo_sqlite,pdo_pgsql,pdo_oci (unstable),pdo_sqlsrv,pdo_sqlsrv,
// mysqli,sqlanywhere,sqlsrv,ibm_db2 (unstable),drizzle_pdo_mysql
'driver' => 'pdo_mysql',
'host' => '127.0.0.1',
'dbname' => 'test',
'user' => 'root',
'password' => '',
'port' => '3306',
];
$a = DatabaseAdapter::newAdapter($config);
$e = new Enforcer('examples/basic_model.conf', $a);
使用自建的adapter
你可以使用自定義的適配器泪蔫,例如:
// GO語言示例
import (
"github.com/casbin/casbin"
"github.com/your-username/your-repo"
)
a := yourpackage.NewAdapter(params)
e := casbin.NewEnforcer("examples/basic_model.conf", a)
在運行時進行加載或保存配置信息
你也許希望重新加載模型,重新加載策略或者在初始化后保存策略
// GO語言示例
// 從模型配置文件中重新加載模型
e.LoadModel()
// 從文件或數(shù)據(jù)庫中重新加載策略
e.LoadPolicy()
// Save the current policy (usually after changed with Casbin API) back to file/database.
// 將當前策略保存到文件或數(shù)據(jù)庫(通常在使用Casbin API之后)
e.SavePolicy()
自動保存
有一個稱為適配器自動保存的功能喘批。當一個適配器支持自動保存功能時撩荣,就意味著它能夠支持從存儲器中添加一條策略或刪除一條策略铣揉。這與savePolicy()不同,后者將刪除存儲中的所有策略規(guī)則餐曹,并將所有策略規(guī)則從casbin Enforcer保存到存儲中逛拱。因此,當策略規(guī)則的數(shù)量很大時台猴,它可能會遇到性能問題朽合。
當適配器支持自動保存時,可以通過enforcer.enableautosave()函數(shù)切換此選項饱狂。默認情況下曹步,該選項處于啟用狀態(tài)(如果適配器支持它)。
需要注意的是:
-
Auto-Save
特性是可選的休讳。 Adapter可以選擇是否實現(xiàn)它箭窜。 -
Auto-Save
只在Casbin enforcer使用的adapter支持它時才有效。 - 查看上述adapter列表中的
AutoSave
列衍腥,查看adapter是否支持Auto-Save
磺樱。
這里有一個關(guān)于自動保存的例子:
// GO語言示例
import (
"github.com/casbin/casbin"
"github.com/casbin/xorm-adapter"
_ "github.com/go-sql-driver/mysql"
)
// By default, the AutoSave option is enabled for an enforcer.
a := xormadapter.NewAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/")
e := casbin.NewEnforcer("examples/basic_model.conf", a)
// Disable the AutoSave option.
e.EnableAutoSave(false)
// Because AutoSave is disabled, the policy change only affects the policy in Casbin enforcer,
// it doesn't affect the policy in the storage.
e.AddPolicy(...)
e.RemovePolicy(...)
// Enable the AutoSave option.
e.EnableAutoSave(true)
// Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer,
// but also affects the policy in the storage.
e.AddPolicy(...)
e.RemovePolicy(...)
更多示例,請參考:https://github.com/casbin/xorm-adapter/blob/master/adapter_test.go
如何編寫 Adapter
所有適配器需要至少實現(xiàn)兩個適配器接口中的兩個方法:LoadPolicy(model model.Model) error
and SavePolicy(model model.Model) error
其他三個功能是可選的婆咸。如果適配器支持自動保存功能竹捉,則應(yīng)該實現(xiàn)它們。
方法 | 類型 | 描述 |
---|---|---|
LoadPolicy() | 強制的 | 從存儲中加載所有策略規(guī)則 |
SavePolicy() | 強制的 | 將所有策略規(guī)則保存到存儲中 |
AddPolicy() | 可選擇的 | 向存儲中添加策略規(guī)則 |
RemovePolicy() | 可選擇的 | 從存儲中刪除策略規(guī)則 |
RemoveFilteredPolicy() | 可選擇的 | 從存儲中刪除匹配篩選器的策略規(guī)則 |
注意:如果適配器不支持自動保存功能尚骄,它應(yīng)該為三個可選功能提供一個空實現(xiàn)块差。如果您不提供它,編譯器將會報錯倔丈。下面是一個例子:
// GO語言示例
// AddPolicy adds a policy rule to the storage.
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
return errors.New("not implemented")
}
// RemovePolicy removes a policy rule from the storage.
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
return errors.New("not implemented")
}
// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
return errors.New("not implemented")
}
casbin執(zhí)行器在調(diào)用這三個可選函數(shù)時將忽略未實現(xiàn)錯誤憨闰。
關(guān)于數(shù)據(jù)庫表結(jié)構(gòu)的創(chuàng)建
作為約定,適配器應(yīng)該能夠自動創(chuàng)建一個名為casbin的數(shù)據(jù)庫(如果它不存在)需五,并將其用于策略存儲鹉动。請使用xorm適配器作為參考實現(xiàn):https://github.com/casbin/xorm-adapter
Watchers
? 我們支持使用像etcd這樣的分布式消息傳遞系統(tǒng)來保持多個casbin執(zhí)行器實例之間的一致性。因此宏邮,我們的用戶可以同時使用多個Casbin enforcers來處理大量的權(quán)限檢查請求泽示。
與策略存儲 adapters類似,我們沒有把watcher的代碼放在主庫中蜜氨。 任何對新消息系統(tǒng)的支持都應(yīng)該作為atcher程序來實現(xiàn)械筛。 完整的Casbin watchers列表如下所示。 歡迎任何第三方對 watcher 進行新的貢獻飒炎,如果有請告知我們埋哟,我將把它放在這個列表中:
Watcher | Type | Author | Description |
---|---|---|---|
Etcd Watcher | KV store | Casbin | Watcher for etcd |
NATS Watcher | Messaging system | Soluto | Watcher for NATS |
ZooKeeper Watcher | KV store | Grepsr | Watcher for Apache ZooKeeper |
Redis Watcher | KV store | @billcobbler | Watcher for Redis |
GCP Pub/Sub Watcher | Messaging system | LivingPackets | Watcher for Google Cloud Platform PUB/SUB |
Role Managers
角色管理器用于管理Casbin中的RBAC角色層次結(jié)構(gòu)(用戶角色映射)。 角色管理器可以從Casbin策略規(guī)則或外部源(如LDAP郎汪、Okta赤赊、Auth0闯狱、Azure AD等) 檢索角色數(shù)據(jù)。 我們支持角色管理器的不同實現(xiàn)砍鸠。 為了保持代碼輕量級扩氢,我們沒有把角色管理器代碼放在主庫中(默認的角色管理器除外)耕驰。 下面提供了Casbin角色管理器的完整列表爷辱。 歡迎任何第三方對角色管理器進行新的貢獻,如果有請告知我們朦肘,我將把它放在這個列表中:
Role manager | Author | Description |
---|---|---|
Default Role Manager (built-in) | Casbin | 支持存儲在Casbin策略中的角色層次結(jié)構(gòu) |
對于開發(fā)人員:所有角色管理器都必須實現(xiàn)RoleManager接口饭弓。默認角色管理器可以用作引用實現(xiàn)。
中間件
WEB框架
Name | Description |
---|---|
Laravel | The PHP framework for web artisans, via plugin: laravel-casbin |
Laravel | An authorization library for the laravel framework, via plugin: Laravel Authorization |
Yii PHP Framework | A fast, secure, and efficient PHP framework, via plugin: yii-casbin |
CakePHP | Build fast, grow solid PHP Framework, via plugin: cake-casbin |
CodeIgniter4 | Associate users with roles and permissions in CodeIgniter4 Web Framework, via plugin: CodeIgniter Permission |
ThinkPHP 5.1 | The ThinkPHP 5.1 framework, via plugin: think-casbin |
ThinkPHP 6.0 | The ThinkPHP 6.0 framework, via plugin: think-authz |
Symfony | The Symfony PHP framework, via plugin: symfony-casbin |
API
管理 API
提供對Casbin策略管理完全支持的基本API媒抠。
參考
全局變量 e
是執(zhí)行者實例弟断。
$e = new Enforcer('examples/rbac_model.conf', 'examples/rbac_policy.csv');
獲取當前策略中顯示的主題列表:
$allSubjects = $e->getAllSubjects();
獲取當前命名策略中顯示的主題列表:
$allNamedSubjects = $e->getAllNamedSubjects("p");
獲取當前策略中顯示的對象列表:
$allObjects = $e->getAllObjects();
獲取當前命名策略中顯示的對象列表:
$allNamedObjects = $e->getAllNamedObjects("p");
獲取當前策略中顯示的操作列表:
$allActions = $e->getAllActions();
獲取當前命名策略中顯示的操作列表:
$allNamedActions = $e->getAllNamedActions("p");
獲取當前策略中顯示的角色列表
$allRoles = $e->getAllRoles();
獲取當前命名策略中顯示的角色列表:
$allNamedRoles = $e->getAllNamedRoles('g');
獲取策略中的所有授權(quán)規(guī)則:
$policy = $e->getPolicy();
獲取策略中的所有授權(quán)規(guī)則,可以指定字段篩選器:
$filteredPolicy = $e->getFilteredPolicy(0, "alice");
獲取命名策略中的所有授權(quán)規(guī)則:
$namedPolicy = $e->getNamedPolicy("p");
獲取命名策略中的所有授權(quán)規(guī)則趴生,可以指定字段過濾器:
$filteredNamedPolicy = $e->getFilteredNamedPolicy("p", 0, "bob");
獲取策略中的所有角色繼承規(guī)則:
$groupingPolicy = $e->getGroupingPolicy();
獲取策略中的所有角色繼承規(guī)則阀趴,可以指定字段篩選器:
$filteredGroupingPolicy = $e->getFilteredGroupingPolicy(0, "alice");
獲取策略中的所有角色繼承規(guī)則:
$namedGroupingPolicy = $e->getNamedGroupingPolicy("g");
獲取策略中的所有角色繼承規(guī)則
$namedGroupingPolicy = $e->getFilteredNamedGroupingPolicy("g", 0, "alice");
確定是否存在授權(quán)規(guī)則
$hasPolicy = $e->hasPolicy('data2_admin', 'data2', 'read');
?
確定是否存在命名授權(quán)規(guī)則
$hasNamedPolicy = $e->hasNamedPolicy("p", "data2_admin", "data2", "read");
向當前策略添加授權(quán)規(guī)則。 如果規(guī)則已經(jīng)存在苍匆,函數(shù)返回false刘急,并且不會添加規(guī)則。 否則浸踩,函數(shù)通過添加新規(guī)則并返回true
$added = $e->addPolicy('eve', 'data3', 'read');
向當前命名策略添加授權(quán)規(guī)則叔汁。 如果規(guī)則已經(jīng)存在,函數(shù)返回false检碗,并且不會添加規(guī)則据块。 否則,函數(shù)通過添加新規(guī)則并返回true:
$added = $e->addNamedPolicy("p", "eve", "data3", "read");
從當前策略中刪除授權(quán)規(guī)則
$removed = $e->removePolicy("alice", "data1", "read");
移除當前策略中的授權(quán)規(guī)則折剃,可以指定字段篩選器另假。 RemovePolicy 從當前策略中刪除授權(quán)規(guī)則:
$removed = $e->removeFilteredPolicy(0, "alice", "data1", "read");
從當前命名策略中刪除授權(quán)規(guī)則:
$removed = $e->removeNamedPolicy("p", "alice", "data1", "read");
從當前命名策略中移除授權(quán)規(guī)則,可以指定字段篩選器:
$removed = $e->removeFilteredNamedPolicy("p", 0, "alice", "data1", "read");
確定是否存在角色繼承規(guī)則
$has = $e->hasGroupingPolicy("alice", "data2_admin");
確定是否存在命名角色繼承規(guī)則:
$has = $e->hasNamedGroupingPolicy("g", "alice", "data2_admin");
向當前策略添加角色繼承規(guī)則怕犁。 如果規(guī)則已經(jīng)存在浪谴,函數(shù)返回false,并且不會添加規(guī)則因苹。 如果規(guī)則已經(jīng)存在苟耻,函數(shù)返回false,并且不會添加規(guī)則:
$added = $e->addGroupingPolicy("group1", "data2_admin");
將命名角色繼承規(guī)則添加到當前策略扶檐。 如果規(guī)則已經(jīng)存在凶杖,函數(shù)返回false,并且不會添加規(guī)則款筑。 否則智蝠,函數(shù)通過添加新規(guī)則并返回true:
$added = $e->addNamedGroupingPolicy("g", "group1", "data2_admin");
從當前策略中刪除角色繼承規(guī)則:
$removed = $e->removeGroupingPolicy("alice", "data2_admin");
從當前策略中移除角色繼承規(guī)則腾么,可以指定字段篩選器:
$removed = $e->removeFilteredGroupingPolicy(0, "alice");
從當前命名策略中移除角色繼承規(guī)則:
$removed = $e->removeNamedGroupingPolicy("g", "alice");
從當前命名策略中移除角色繼承規(guī)則,可以指定字段篩選器:
$removed = $e->removeFilteredNamedGroupingPolicy("g", 0, "alice");
添加自定義函數(shù):
func customFunction($key1, $key2) {
if ($key1 == "/alice_data2/myid/using/res_id" && $key2 == "/alice_data/:resource") {
return true;
} elseif ($key1 == "/alice_data2/myid/using/res_id" && $key2 == "/alice_data2/:id/using/:resId") {
return true;
} else {
return false;
}
}
func customFunctionWrapper(...$args){
$key1 := $args[0];
$key2 := $args[1];
return customFunction($key1, $key2);
}
$e->addFunction("keyMatchCustom", customFunctionWrapper);
RBAC API
一個更友好的RBAC API杈湾。 這個API是Management API的子集解虱。 RBAC用戶可以使用這個API來簡化代碼。
參考
全局變量 e
是實施者實例漆撞。
$e = new Enforcer('examples/rbac_model.conf', 'examples/rbac_policy.csv');
獲取用戶具有的角色:
$res = $e->getRolesForUser("alice");
獲取具有角色的用戶:
$res = $e->getUsersForRole("data1_admin");
確定用戶是否具有角色:
$res = $e->hasRoleForUser("alice", "data1_admin");
為用戶添加角色殴泰。 如果用戶已經(jīng)擁有該角色(aka不受影響),則返回false:
$e->addRoleForUser("alice", "data2_admin");
刪除用戶的角色浮驳。 如果用戶沒有該角色(aka不受影響)悍汛,則返回false:
$e->deleteRoleForUser("alice", "data1_admin");
刪除用戶的所有角色。 如果用戶沒有任何角色(aka不受影響)至会,則返回false:
$e->deleteRolesForUser("alice");
刪除一個用戶离咐。 如果用戶不存在,則返回false(也就是說不受影響):
$e->deleteUser("alice");
刪除一個角色:
$e->deleteRole("data2_admin");
刪除權(quán)限奉件。 如果權(quán)限不存在宵蛀,則返回false(aka不受影響):
$e->deletePermission("read");
為用戶或角色添加權(quán)限。 如果用戶或角色已經(jīng)擁有該權(quán)限(aka不受影響)县貌,則返回false:
$e->addPermissionForUser("bob", "read");
刪除用戶或角色的權(quán)限术陶。 如果用戶或角色沒有權(quán)限(aka不受影響),則返回false:
$e->deletePermissionForUser("bob", "read");
刪除用戶或角色的權(quán)限窃这。 如果用戶或角色沒有任何權(quán)限(aka不受影響)瞳别,則返回false:
$e->deletePermissionsForUser("bob");
獲取用戶或角色的權(quán)限:
$e->getPermissionsForUser("bob");
確定用戶是否具有權(quán)限:
$e->hasPermissionForUser("alice", []string{"read"});
獲取用戶具有的隱式角色。 與GetRolesForUser() 相比杭攻,該函數(shù)除了直接角色外還檢索間接角色:
例如:
g, alice, role:admin
g, role:admin, role:userGetRolesForUser("alice") 只能獲取到: ["role:admin"].
But GetImplicitRolesForUser("alice") 卻能獲取到: ["role:admin", "role:user"].
$e->getImplicitRolesForUser("alice");
獲取用戶或角色的隱式權(quán)限祟敛。與getPermissionsForuser()相比,此函數(shù)檢索繼承角色的權(quán)限
p, admin, data1, read
p, alice, data2, read
g, alice, adminGetPermissionsForUser("alice") 只能獲取到: [["alice", "data2", "read"]].
But GetImplicitPermissionsForUser("alice") 卻能獲取到: [["admin", "data1", "read"], ["alice", "data2", "read"]].
$e->getImplicitPermissionsForUser("alice");