目录

GO第三方库-casbin

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 支持从代码、字符串和上面提到的配置文件方式加载。 “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 功能就已经够我们用了,更多的功能只有等以后有需求了再去发掘。