Casbin 是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型.
动机
最近在研究怎么使用 golang 开发一个管理后台的后端时,不知道怎么对 api 做访问控制,经查找发现 casbin 库支持常用的几种访问控制模型,如 ACL/RBAC/ABAC 等。可以实现灵活的访问权限控制。于是打算对其进行研究一番。本篇内容基于官方文档,如果有兴趣可以自行官网查阅。
开始使用
要创建一个 casbin 执行器,我们需要提供一个 Model 和一个 Adapter。
1
2
3
|
import "github.com/casbin/casbin/v2"
e, err := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")
|
-
model 简单来说,就是定义一些准入模型。
-
adapter 就是存储用户的准入策略。
除了通过文件加载的方式载入 model 和 adapter 之外,还支持其他类型的方式。
model 的语法
request 的定义,表示的是请求的数据。
1
2
|
[request_definition]
r = sub, obj, act
|
policy 的定义,表示的是用户的数据。
1
2
|
[policy_definition]
p = sub, obj, act
|
对 policy 生效范围的定义,以下示例展示了只要有一条规则生效,则最终的结果是放行。
1
2
|
[policy_effect]
e = some(where (p.eft == allow))
|
matchers 是策略匹配器的定义,定义怎么匹配规则。
1
2
|
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
|
我们可以用以下场景加强理解。
-
比如 r.sub == p.sub ,可以理解为,请求过来的用户名是否跟策略里面的用户名匹配。
-
比如 r.obj == p.obj ,可以理解为,请求过来的 url 是否跟策略里面的 url 匹配。
-
比如 r.act == p.act ,可以理解为,请求过来的 method 是否跟策略里面的 method 匹配。
官方用例
官网给的基础的基于 acl 的访问控制。我们可以看看。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import (
"log"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v2"
_ "github.com/go-sql-driver/mysql"
)
// 使用MySQL数据库初始化一个Xorm适配器
a, err := xormadapter.NewAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/")
if err != nil {
log.Fatalf("error: adapter: %s", err)
}
m, err := model.NewModelFromString(`
[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
`)
if err != nil {
log.Fatalf("error: model: %s", err)
}
e, err := casbin.NewEnforcer(m, a)
if err != nil {
log.Fatalf("error: enforcer: %s", err)
}
|
检查权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
sub := "alice" // 想要访问资源的用户。
obj := "data1" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// 处理err
}
if ok == true {
// 允许alice读取data1
} else {
// 拒绝请求,抛出异常
}
// 您可以使用BatchEnforce()来批量执行一些请求
// 这个方法返回布尔切片,此切片的索引对应于二维数组的行索引。
// 例如results[0] 是{"alice", "data1", "read"}的结果
results, err := e.BatchEnforce([][]interface{}{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"jack", "data3", "read"}})
|
基于 RBAC
rbac 的访问控制相比 acl ,多了一个角色的定义。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[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.act == p.act && (keyMatch(r.obj, p.obj) || keyMatch2(r.obj, p.obj))
|
代码逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package main
import (
// "fmt"
"learning/common"
)
func main() {
common.InitMysql()
common.InitCasbinEnforcer()
// 新增一条角色规则
common.CasbinEnforcer.AddPermissionForUser("admin", "/login", "POST")
// 将 user 加入到该角色
common.CasbinEnforcer.AddRoleForUser("test1", "admin")
err := common.CasbinEnforcer.LoadPolicy()
if err != nil {
fmt.Println(err)
}
// 验证一下用户是否准入
b, err:= common.CasbinEnforcer.Enforce("test1", "/login", "POST")
if err != nil {
fmt.Println(err)
}
fmt.Println(b)
// 删除角色绑定
common.CasbinEnforcer.DeleteRoleForUser("test1", "admin")
}
|
总结
casbin 还有很多的功能没有使用到,它还有其他的访问控制模型,和扩展功能,不过对现在的我们来说,它的 rbac 功能就已经够我们用了,更多的功能只有等以后有需求了再去发掘。