三十的博客

Go Casbin 学习

本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
发布时间
阅读量 加载中...

Casbin 基础概念

什么是 Casbin

Casbin 是一个授权库,用于处理各种复杂的访问控制场景。

Casbin - 中文文档

它支持:

核心概念

在 Casbin 的授权流程中:

  1. ​[matchers]先执行 ​:负责筛选出所有相关的策略规则
  2. ​[policy_effect]后执行 ​:负责解释这些匹配到的规则

安装 Casbin

bash
go get github.com/casbin/casbin/v2

最简单的 ACL 示例

go
package main

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

func main() {
	// 创建enforcer,加载模型和策略文件
	e, err := casbin.NewEnforcer("model.conf", "policy.csv")
	if err != nil {
		panic(err)
	}

	// 检查权限
	sub := "alice" // 用户
	obj := "data1" // 资源
	act := "read"  // 操作

	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		panic(err)
	}

	if ok {
		fmt.Println("允许访问")
	} else {
		fmt.Println("拒绝访问")
	}
}

ACL 模型文件

ini
[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

ACL 策略文件

csv
p, alice, data1, read
p, bob, data2, write

模型、策略文件配置理解

model.conf 配置

model.conf - 模型定义文件

模型文件定义了访问控制的基本结构和逻辑,它由多个部分组成。

[request_definition] - 请求定义
ini
[request_definition]
r = sub, obj, act

定义了访问请求的格式,通常包含三个元素:

可以扩展更多字段,如:r = sub, obj, act, time

[policy_definition] - 策略定义
ini
[policy_definition]
p = sub, obj, act

定义了策略的存储格式,与请求定义对应

在 RBAC 中可能还包含角色:p = sub, obj, act 或 p = role, obj, act

[role_definition] - 角色定义

RBAC 专用

ini
[role_definition]
g = _, _

这行配置定义了:

g 是角色关系的名称(可以自定义,如 group)

_,_表示关系的两端:用户, 角色

实际意义

这表示:

例如在策略中:

csv
g, alice, admin

表示:用户 alice 拥有 admin 角色。

[policy_effect] - 策略效果

定义了多个策略规则如何组合生效。

eft 是"effect"的缩写,表示这条策略规则的效果。

不看用户名 ​:策略效果评估时,不区分是哪个用户的规则。

只看规则存在 ​:只要存在任何 deny 规则,!some(deny)就会为 false。

常见选项:

ini
[policy_effect]
e = some(where (p.eft == allow))
策略效果详解

some(where (p.eft == allow))

​ 含义:

示例:

csv
p, alice, data1, read, allow
p, alice, data1, read, deny

结果:允许访问(因为有一条 allow 规则)


!some(where (p.eft == deny))

​ 含义:

示例 1:

csv
p, alice, data1, read, allow
p, bob, data1, read, allow

结果:允许访问(因为没有 deny 规则)

示例 2:

csv
p, alice, data1, read, allow
p, bob, data1, read, allow
p, alice, data1, read, deny

结果:拒绝访问(因为有 deny 规则)


some(where (p.eft == allow)) && !some(where (p.eft == deny))

​ 含义:

示例 1:

csv
p, alice, data1, read, allow
p, bob, data1, read, allow

结果:允许访问(有 allow 且没有 deny)

示例 2:

csv
p, alice, data1, read, deny
p, bob, data1, read, deny

结果:拒绝访问(没有 allow)


[matchers]-匹配器

定义了请求如何匹配策略的核心逻辑。

ini
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
  1. r.sub == p.sub:请求的主体(subject)必须匹配策略的主体
  2. r.obj == p.obj:请求的资源(object)必须匹配策略的资源
  3. r.act == p.act:请求的操作(action)必须匹配策略的操作

policy.csv 配置

策略文件存储了具体的访问控制规则,其格式由模型文件中的[policy_definition]决定。

基本 ACL 策略

对于基本 ACL 模型:

csv
p, alice, data1, read
p, bob, data2, write

每行代表一条策略规则

RBAC 策略

对于 RBAC 模型,通常有两类策略:

  1. 权限分配策略:
csv
p, admin, data1, read
p, admin, data1, write
p, user, data1, read

表示角色对资源的权限

  1. 用户角色分配策略:
csv
g, alice, admin
g, bob, user

策略文件中的特殊语法

通配符 ​:可以使用*匹配任意值

csv
p, admin, *, *

否定​:可以在 eft 字段指定 allow/deny

csv
p, alice, data1, read, deny

优先级​:可以使用优先级数字

csv
p, admin, data1, read, allow, 1

配置示例解析

ACL 示例

model.conf
ini
[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
csv
p, alice, data1, read
p, bob, data2, write

逻辑解释 ​:

  1. 当请求(alice, data1, read)时:
  1. 当请求(alice, data2, read)时:

RBAC 示例

model.conf
ini
[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
policy.csv
csv
p, admin, data1, read
p, admin, data1, write
p, user, data1, read
g, alice, admin
g, bob, user

逻辑解释 ​:

  1. 当请求(alice, data1, read)时:
  1. 当请求(bob, data1, write)时:

理解模型和策略的关系

RBAC 示例

go
package main

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

func main() {
	e, err := casbin.NewEnforcer("rbac_model.conf", "rbac_policy.csv")
	if err != nil {
		panic(err)
	}

	// 检查用户角色
	roles, err := e.GetRolesForUser("alice")
	if err != nil {
		panic(err)
	}
	fmt.Println("Alice的角色:", roles)

	// 检查权限
	sub := "alice"
	obj := "data2"
	act := "read"

	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		panic(err)
	}

	if ok {
		fmt.Println("允许访问")
	} else {
		fmt.Println("拒绝访问")
	}
}

RBAC 模型文件

ini
[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

RBAC 策略文件

csv
p, admin, data1, read
p, admin, data1, write
p, admin, data2, read
p, admin, data2, write
p, user, data1, read
g, alice, admin
g, bob, user

使用数据库存储策略

实际项目中,我们通常将策略存储在数据库中:

go
package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/gorm-adapter/v3"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func main() {
	// 初始化数据库连接
	dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	// 创建适配器
	adapter, err := gormadapter.NewAdapterByDB(db)
	if err != nil {
		panic(err)
	}

	// 创建enforcer
	e, err := casbin.NewEnforcer("rbac_model.conf", adapter)
	if err != nil {
		panic(err)
	}

	// 添加策略
	e.AddPolicy("admin", "data1", "read")
	e.AddPolicy("admin", "data1", "write")
	e.AddRoleForUser("alice", "admin")

	// 保存策略
	err = e.SavePolicy()
	if err != nil {
		panic(err)
	}

	// 检查权限
	ok, err := e.Enforce("alice", "data1", "read")
	if err != nil {
		panic(err)
	}
	fmt.Println("允许访问:", ok)
}

高级功能

ABAC (基于属性的访问控制)

Casbin 支持 ABAC,可以根据对象的属性进行访问控制:

go
type Book struct {
	Name   string
	Author string
}

func main() {
	e, err := casbin.NewEnforcer("abac_model.conf")
	if err != nil {
		panic(err)
	}

	book := Book{Name: "Casbin指南", Author: "alice"}

	// 检查权限
	ok, err := e.Enforce("alice", book, "read")
	if err != nil {
		panic(err)
	}
	fmt.Println("允许访问:", ok)
}

ABAC 模型文件 (abac_model.conf):

ini
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

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

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

使用函数进行复杂匹配

可以在模型中使用自定义函数:

go
e, err := casbin.NewEnforcer("model_with_func.conf")
if err != nil {
    panic(err)
}

// 添加自定义函数
e.AddFunction("timeMatch", func(args ...interface{}) (interface{}, error) {
    // 实现时间匹配逻辑
    return true, nil
})

然后在模型中使用:

ini
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act && timeMatch(r.time)

调试技巧

使用 Enforcer 的打印方法查看加载的模型和策略 ​:

go
fmt.Println(e.GetModel())
fmt.Println(e.GetPolicy())

检查角色映射关系 ​:

go
roles, _ := e.GetRolesForUser("alice")
fmt.Println(roles)

启用日志 ​:

go
e.EnableLog(true)
#Casbin #Golang