简介
权限管理在几乎每个系统中都是必备的模块。如果项目开发每次都要实现一次权限管理,无疑会浪费开发时间,增加开发成本。因此,casbin
库出现了。casbin
是一个强大、高效的访问控制库。支持常用的多种访问控制模型,如ACL/RBAC/ABAC
等。可以实现灵活的访问权限控制。同时,casbin
支持多种编程语言,Go/Java/Node/PHP/Python/.NET/Rust
。我们只需要一次学习,多处运用。
快速使用
我们依然使用 Go Module 编写代码,先初始化:
$ mkdir casbin && cd casbin
$ go mod init github.com/darjun/go-daily-lib/casbin
然后安装casbin
,目前是v2
版本:
$ go get github.com/casbin/casbin/v2
权限实际上就是控制谁能对什么资源进行什么操作。casbin
将访问控制模型抽象到一个基于 PERM(Policy,Effect,Request,Matchers) 元模型的配置文件(模型文件)中。因此切换或更新授权机制只需要简单地修改配置文件。
policy
是策略或者说是规则的定义。它定义了具体的规则。
request
是对访问请求的抽象,它与e.Enforce()
函数的参数是一一对应的
matcher
匹配器会将请求与定义的每个policy
一一匹配,生成多个匹配结果。
effect
根据对请求运用匹配器得出的所有结果进行汇总,来决定该请求是允许还是拒绝。
下面这张图很好地描绘了这个过程:
我们首先编写模型文件:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
[policy_effect]
e = some(where (p.eft == allow))
上面模型文件规定了权限由sub,obj,act
三要素组成,只有在策略列表中有和它完全相同的策略时,该请求才能通过。匹配器的结果可以通过p.eft
获取,some(where (p.eft == allow))
表示只要有一条策略允许即可。
然后我们策略文件(即谁能对什么资源进行什么操作):
p, dajun, data1, read
p, lizi, data2, write
上面policy.csv
文件的两行内容表示dajun
对数据data1
有read
权限,lizi
对数据data2
有write
权限。
接下来就是使用的代码:
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
代码其实不复杂。首先创建一个casbin.Enforcer
对象,加载模型文件model.conf
和策略文件policy.csv
,调用其Enforce
方法来检查权限。运行程序:
$ go run main.go
dajun CAN read data1
lizi CAN write data2
dajun CANNOT write data1
dajun CANNOT read data2
请求必须完全匹配某条策略才能通过。("dajun", "data1", "read")
匹配p, dajun, data1, read
,("lizi", "data2", "write")
匹配p, lizi, data2, write
,所以前两个检查通过。第 3 个因为"dajun"
没有对data1
的write
权限,第 4 个因为dajun
对data2
没有read
权限,所以检查都不能通过。输出结果符合预期。
sub/obj/act
依次对应传给Enforce
方法的三个参数。实际上这里的sub/obj/act
和read/write/data1/data2
是我自己随便取的,你完全可以使用其它的名字,只要能前后一致即可。
上面例子中实现的就是ACL
(access-control-list,访问控制列表)。ACL
显示定义了每个主体对每个资源的权限情况,未定义的就没有权限。我们还可以加上超级管理员,超级管理员可以进行任何操作。假设超级管理员为root
,我们只需要修改匹配器:
[matchers]
e = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
只要访问主体是root