Casbin学习

125 阅读7分钟

Casbin基本使用

casbin官网

什么是Casbin?

Casbin是一个授权库,可以在我们希望某个对象或实体被特定用户或主体访问的流程中使用。 访问类型,即动作,可以是_读取_,写入删除,或者由开发者设置的任何其他动作。 这就是Casbin最广泛使用的方式,它被称为“标准”或经典的{ 主体, 对象, 动作 }流程。

Casbin能够处理许多复杂的授权场景,而不仅仅是标准流程。 可以添加角色(RBAC),属性(ABAC)等。

Casbin做什么

  1. 在经典的{ 主体, 对象, 动作 }形式或者你定义的自定义形式中执行策略。 支持允许和拒绝授权。
  2. 处理访问控制模型及其策略的存储。
  3. 管理角色-用户映射和角色-角色映射(也称为RBAC中的角色层次)。
  4. 支持内置的超级用户,如rootadministrator。 超级用户可以在没有明确权限的情况下做任何事情。
  5. 提供多个内置操作符以支持规则匹配。 例如,keyMatch可以将资源键/foo/bar映射到模式/foo*

Casbin 做什么

  1. 身份验证(也就是在用户登录时验证用户名密码
  2. 管理用户或角色列表。

Casbin模型基础

PERM元模型

定义一个策略、定义一个匹配规则,通过一个请求过来的参数与策略通过匹配规则进行匹配获得一个影响,拿到影响的结果进到影响的表达式返回一个布尔值

Policy 策略

[policy_definition]是策略的定义。 它定义了策略的含义。 例如,我们有以下模型:

[policy_definition]
p = sub, obj, act
p2 = sub, act

我们有以下策略(如果在策略文件中):

p, alice, data1, read
p2, bob, write-all-objects

策略中的每一行都被称为策略规则。 每个策略规则都以策略类型开始,如pp2。 如果有多个定义,它用于匹配策略定义。 上述策略显示了以下绑定。 绑定可以在匹配器中使用。

(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)

Effect 影响

[policy_effect]是策略效果的定义。 如果多个策略规则匹配请求,它决定是否应批准访问请求。 例如,一条规则允许,另一条规则拒绝。

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

上述策略效果意味着,如果有任何匹配的allow策略规则,最终效果是allow(也称为允许覆盖)。 p.eft是策略的效果,它可以是allowdeny。 这是可选的,其默认值是allow。 由于我们在上面没有指定它,所以它使用默认值。

策略效果的另一个例子是:

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

这意味着,如果没有匹配的deny策略规则,最终的效果是allow(也称为deny-override)。 some意味着存在一个匹配的策略规则。 any意味着所有匹配的策略规则(这里未使用)。 策略效果甚至可以与逻辑表达式连接:

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

这意味着必须至少有一个匹配的allow策略规则,并且不能有任何匹配的deny策略规则。 因此,以这种方式,支持允许和拒绝授权,并且拒绝优先。

支持的内置策略效果有:

策略效果含义例子
some(where (p.eft == allow))allow-overrideACL, RBAC, etc.
!some(where (p.eft == deny))deny-overrideDeny-override
some(where (p.eft == allow)) && !some(where (p.eft == deny))allow-and-denyAllow-and-deny
priority(p.eft) || denypriorityPriority
subjectPriority(p.eft)基于角色的优先级主题-优先级

Request 请求

[request_definition]部分定义了e.Enforce(...)函数中的参数。

[request_definition]
r = sub, obj, act

在这个例子中,subobjact代表了经典的访问三元组:主体(访问实体),对象(被访问资源)和动作(访问方法)。 然而,你可以自定义你自己的请求格式。 例如,如果你不需要指定特定的资源,你可以使用sub, act,或者如果你有两个访问实体,你可以使用sub, sub2, obj, act

Matchers 规则

[匹配器]是策略匹配器的定义。 匹配器是定义如何根据请求评估策略规则的表达式。

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

上述匹配器是最简单的,意味着请求中的主题、对象和动作应与策略规则中的相匹配。

匹配器中可以使用算术运算符如+, -, *, /和逻辑运算符如&&, ||, !

示例

image-20240609101830754.png

Role 角色域

[role_definition]
g = _,_   表示角色为基础          (_,_ -> 用户是哪个角色)
g = _,_,_ 表示域为基础(多商户模式) (_,_,_ -> 用户是哪个角色属于哪个商户)

示例

image-20240609103532021.png

g(r.sub, p.sub) --> (alice -> data2_admin)
相当于使用alice匹配一下,使用data2_admin匹配一下

image-20240609103857757.png

g(r.sub, p.sub, r.dom) --> (alice -> admin -> domain1)

Go使用Casbin

安装casbin

go get github.com/casbin/casbin/v2

初始化项目

mkdir casbin-test && go mod init casbin-test

创建model.conf &&policy.csv

## 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

## policy.csv
p, admin, data1, read

示例

// main.go
package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
)

func main1() {
	e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")

	sub := "admin" // the user that wants to access a resource.
	obj := "data1" // the resource that is going to be accessed.
	act := "read"  // the operation that the user performs on the resource.
    
	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		// handle err
		fmt.Println("error: %s", err)
	}
	if ok == true {
		// permit alice to read data1
		fmt.Println("通过")
	} else {
		// deny the request, show an error
		fmt.Println("未通过")
	}
}

使用adapter

image-20240609160841021.png

使用GORMAdapter,将policy.csv数据用数据库存储

安装gorm-adapter

go get github.com/casbin/gorm-adapter/v3

示例

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	a, _ := gormadapter.NewAdapter("mysql", "root:root123456@tcp(127.0.0.1:3306)/casbin", true) // Your driver and data source.
	e, _ := casbin.NewEnforcer("./model.conf", a)

	sub := "alice" // the user that wants to access a resource.
	obj := "data2" // the resource that is going to be accessed.
	act := "read"  // the operation that the user performs on the resource.
	// 增加一个新的策略
	added, err := e.AddPolicy(sub, obj, act)
	fmt.Println(added, err)
    
	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
        fmt.Println("error: %s", err)
	}
	if ok == true {
		fmt.Println("通过")
	} else {
		fmt.Println("未通过")
	}

}

casbin 增删改查API

casbin API

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	a, _ := gormadapter.NewAdapter("mysql", "root:root123456@tcp(127.0.0.1:3306)/casbin", true) // Your driver and data source.
	e, _ := casbin.NewEnforcer("./model.conf", a)

	policy, _ := e.GetPolicy()
	fmt.Println(policy)

	// 添加一个策略并且查看是否有这个策略
	e.AddPolicy("added_user", "data1", "read")
	hasPolicy, _ := e.HasPolicy("added_user", "data1", "read")
	fmt.Println(hasPolicy)

	e.AddPolicy("user", "data2", "write") // 用于删除
	// 删除一个策略并且查看是否有这个策略
	e.RemovePolicy("user", "data2", "write")
	hasPolicy, _ = e.HasPolicy("added_user", "data1", "read")
	fmt.Println(hasPolicy)

	// 更新策略并且查看是否有这个策略
	e.UpdatePolicy([]string{"added_user", "data1", "read"},
		[]string{"added_user", "data1", "write"})
	hasPolicy, _ = e.HasPolicy("added_user", "data1", "write")
	fmt.Println(hasPolicy)
	// 查看所有policy
	ps, _ := e.GetPolicy()
	fmt.Println(ps)
}

使用自定义函数

首先,准备你的函数。 它接受几个参数并返回一个布尔值:

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{}类型包装它:

func KeyMatchFunc(args ...interface{}) (interface{}, error) {
    name1 := args[0].(string)
    name2 := args[1].(string)

    return (bool)(KeyMatch(name1, name2)), nil
}

最后,将函数注册到Casbin执行器:

e.AddFunction("my_func", KeyMatchFunc)

现在,你可以在你的模型rbac_model.conf中像这样使用函数:

[matchers]
m = g(r.sub, p.sub) && my_func(r.obj, p.obj) && r.act == p.act

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) && my_func(r.obj, p.obj) && r.act == p.act

示例(RBAC)

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	a, _ := gormadapter.NewAdapter("mysql", "root:root123456@tcp(127.0.0.1:3306)/casbin", true)
	e, _ := casbin.NewEnforcer("./rbac_model.conf", a)

	e.AddFunction("my_func", KeyMatchFunc)

	//// 为用户添加一个角色
	//e.AddRoleForUser("test", "added_user")
	//e.AddPolicy("added_user", "data2", "read")

	if ok, _ := e.Enforce("test", "data2", "read"); ok {
		fmt.Println("通过")
	} else {
		fmt.Println("未通过")
	}
}

func KeyMatch(key1 string, key2 string) bool {
	return key1 == key2
}

func KeyMatchFunc(args ...interface{}) (interface{}, error) {
	name1 := args[0].(string)
	name2 := args[1].(string)

	return (bool)(KeyMatch(name1, name2)), nil
}

支持的内置函数

函数url模式例子
keyMatch/alice_data/resource1这样的URL路径/alice_data/*这样的URL路径或*模式keymatch_model.conf/keymatch_policy.csv
keyMatch2/alice_data/resource1这样的URL路径/alice_data/:resource这样的URL路径或:模式keymatch2_model.conf/keymatch2_policy.csv
keyMatch3/alice_data/resource1这样的URL路径/alice_data/{resource}这样的URL路径或{}模式github.com/casbin/casb…
keyMatch4/alice_data/123/book/123这样的URL路径/alice_data/{id}/book/{id}这样的URL路径或{}模式github.com/casbin/casb…
keyMatch5/alice_data/123/?status=1这样的URL路径/alice_data/{id}/*这样的URL路径,{}*模式github.com/casbin/casb…
regexMatch任何字符串一个正则表达式模式keymatch_model.conf/keymatch_policy.csv
ipMatch192.168.2.123这样的IP地址192.168.2.0/24这样的IP地址或CIDRipmatch_model.conf/ipmatch_policy.csv
globMatch/alice_data/resource1这样的路径样式路径/alice_data/*这样的glo