授权框架 Casbin

2,147 阅读4分钟

Casbin

Casbin 是一个权限访问控制的开源框架,能够基于各种访问控制模型实现权限控制功能;也就是说它只提供了授权功能,但是没有认证功能

Casbin 实现了基于 ACL ( Access control list ),RBAC( Role-Based Access Control),ABAC( Attribute-Based access control )的权限访问控制

Casbin 结构

Casbin 主要有三部分组成:Access Control Model,Policy Document,Enforcer

Access Control Model

Casbin 的访问控制模型被抽象成了一个配置文件,这个配置文件由以下四部分组成

  1. Request:定义了在 Enforcer.Enforce(下面会介绍到) 方法中请求的参数和这些传入参数的顺序;一个基本的 Request 由一个三元组组成:[subject,obj,act],subject 是指访问的实体,也就是用户;obj 是指请求的资源,act 是指对这个资源的操作,定义如下:

    #model.conf

    [request_definition]
    r = sub,obj,act
    
  2. Policy:定义了访问策略的模型,其实就是定义了在 Policy Document 中策略规则的字段名称以及顺序,定义如下:

    #model.conf

    [policy_definition]
    p = sub,obj,act
    
  3. Matcher:定义了 request 和 policy 之间的匹配规则,例如:

    #model.conf

    [matchers]
    m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
    

    上面的这个匹配规则就是当请求的参数 (r.sub,r.obj,r.act) 在定义的策略文件中能找到,说明就匹配成功了,返回的结果会存放在 p.eft 当中

  4. Effect

    Effect 可以说是在 Matcher 的匹配结果之上,再次进行逻辑组合判断,判断的结果才是该用户是否有操作权限的结果

    下面是一个例子:

    #model.conf

    [policy_effect]
    e = some(where (p.eft == allow))
    

    上面这个逻辑表达式的意思就是说:当在 matcher匹配的结果中存在任何一个 p.eft == allow 的结果,那么这个逻辑表达式的结果就为 true

  5. Role (optional)

    上面的四个是最基本的,如果使用 RBAC 的 Access model,那么还需要 Role 模型的定义,就是定义用户角色的模型,如下所示:#model.conf

    [role_definition]
    g = _,_
    
Policy Document (策略文档)

策略文档就是根据 Access Control Model 中定义的 [policy_definition] 生成的一条条 policy rule (策略规则),比如:#policy.csv

p,alice,data1,read  // 表示:alice 可以 read data1
p,bob,data2,write   // 表示:bob 可以 write data2

如果是使用 RBAC model,那么还会在这个文件中根据 [role_definition] 生成用户和角色的实例,比如:#policy.csv

p,alice,data1,read
p,bob,data2,read
p,data2_admin,data2,read   //表示 data2_admin 可以 read data2
p,data2_admin,data2,write  //表示 data2_admin 可以 write data2
​
g,alice,data2_admin  //表示 alice 是 data2_admin
Enforcer

Enforcer 就是一个执行器,通过上面的两个文件 model.conf 和 policy.csv 构造出一个执行器,通过这个执行器来判断这个用户对于特定的资源有没有对应的操作权限

首先,构造一个 Enforcer:

import (
    "github.com/casbin/casbin/v2"
    "fmt"
)
​
func main(){
    // construct a enforcer
    e, err := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")
    if err != nil{
        return
    }
    // set a access operation
    sub := "alice"
    obj := "data1"
    act := "read"
    //enforce permission check
    ok,err := e.Enforce(sub,obj,act)
    if err != nil{
        return
    }
    if ok{
        fmt.Println("matched")
    }else{
        fmt.Println("mismatched")
    }
}

Storage

Casbin 会存储 Access model 和 Policy Document,上面的例子中 Access model 是存储在 model.conf 文件当中的,而 policy Document 是存储在 policy.csv 当中的;

Casbin 也可以通过其他方式存储 Access model 和 Policy Document,下面来一一介绍;

Access Model 的三种加载方式

1. 从 .Conf 配置文件加载 Access model

这是 Casbin 最常用的方式,例如:examples/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

加载文件

e := casbin.NewEnforcer("examples/model.conf", "examples/rbac_policy.csv")
2. 从代码加载

这就是通过代码动态初始化,而不是使用 .CONF 文件,代码如下:

import (
    "github.com/casbin/casbin/v2"
    "github.com/casbin/casbin/v2/model"
    "github.com/casbin/casbin/v2/persist/file-adapter"
)
​
// Initialize the model from Go code.
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")
​
// Load the policy rules from the .CSV file adapter.
// Replace it with your adapter to avoid files.
a := fileadapter.NewAdapter("examples/rbac_policy.csv")
​
// Create the enforcer.
e := casbin.NewEnforcer(m, a)
3. 从 string 加载

这种方式就是将 Access model 存储在一个字符串变量中,如下:

import (
    "github.com/casbin/casbin/v2"
    "github.com/casbin/casbin/v2/model"
)
​
// 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, _ := model.NewModelFromString(text)
​
// Load the policy rules from the .CSV file adapter.
// Replace it with your adapter to avoid files.
a := fileadapter.NewAdapter("examples/rbac_policy.csv")
​
// Create the enforcer.
e := casbin.NewEnforcer(m, a)

Policy Document 加载

1 .通过 .CSV 加载

这是最常用的方式,就是将所属有的权限全部定义在一个 CSV 文件的当中,然后使用 Casbin.Enforcer 加载

p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin
2. 存储 Adapter

Casbin 支持各种 Adapter,比如:gorm Adapter,beego orm Adapter等,可以将 Policy 存储到数据库当中,下面是存储结构

Policy file

p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, admin

对应的数据库结构:( 如:Mysql)

idptypev0v1v2v3v4v5
1pdata2_admindata2read
2pdata2_admindata2write
3galiceadmin

字段:

  • id:主键,生成方式是依赖于指定的 Adapter
  • ptype:对应于 p,g,g2 等
  • v0-v5:这些字段没有指定的意义,对应于 policy.csv 当中从左到右的值;目前基本上所有的 Adapter只是显示了最多六个字段

对于 Casbin policy 目前支持的 Adapter,可以通过:casbin.org/docs/en/ada…,找到

3. 自定义存储

上面是官方提供的 policy Adapter,当然你也可以实现下面这些接口来自定义存储

LoadPolicy()    basic   Load all policy rules from the storage
SavePolicy()    basic   Save all policy rules to the storage
AddPolicy() optional    Add a policy rule to the storage
RemovePolicy()  optional    Remove a policy rule from the storage
RemoveFilteredPolicy()  optional    Remove policy rules that match the filter from the storage

参考:casbin.org/docs/en/ove…