侧边栏壁纸
博主头像
憨憨大头个人博客博主等级

心存希冀,目有繁星

  • 累计撰写 110 篇文章
  • 累计创建 13 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

golang中使用casbin权限控制

Administrator
2024-09-03 / 0 评论 / 2 点赞 / 221 阅读 / 16551 字

权限框架casbin

官方文档

1.概述

Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。

Casbin支持以下编程语言:

img

  • Casbin可以做到:
  1. 支持自定义请求的格式,默认的请求格式为{subject, object, action}
  2. 具有访问控制模型model和策略policy两个核心概念。
  3. 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。
  4. 支持超级用户,如 rootAdministrator,超级用户可以不受授权策略的约束访问任意资源。
  5. 支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo*
  • Casbin不能做到:
  1. 身份认证 authentication(即验证用户的用户名、密码),casbin只负责访问控制。应该有其他专门的组件负责身份认证,然后由casbin进行访问控制,二者是相互配合的关系。
  2. 管理用户列表或角色列表。 Casbin 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系

github:https://github.com/casbin/casbin

docs:https://casbin.org/docs/zh-CN/overview

2.工作原理

在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。

PERM模型

PERM(Policy, Effect, Request, Matchers)模型很简单, 但是反映了权限的本质 – 访问控制

  • Policy: 定义权限的规则
  • Effect: 定义组合了多个 Policy 之后的结果, allow/deny
  • Request: 访问请求, 也就是谁想操作什么
  • Matcher: 判断 Request 是否满足 Policy

Module File 语法

casbin 是基于 PERM 的, 所有 model file 中主要就是定义 PERM 4 个部分

1.Request definition

[request_definition]
r = sub, obj, act

分别表示 request 中的

  • accessing entity (Subject)
  • accessed resource (Object)
  • the access method (Action)

2.Policy definition

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

定义的每一行称为 policy rule, p, p2 是 policy rule 的名字. p2 定义的是 sub 所有的资源都能执行 act

3.Policy effect

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

上面表示有任意一条 policy rule 满足, 则最终结果为 allow

4.Matchers

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

定义了 request 和 policy 匹配的方式, p.eft 是 allow 还是 deny, 就是基于此来决定的

5.Role

[role_definition]
g = _, _
g2 = _, _
g3 = _, _, _

g, g2, g3 表示不同的 RBAC 体系, _, _ 表示用户和角色 _, _, _ 表示用户, 角色, 域

您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中获得RBAC角色和ABAC属性,并共享一组policy规则。

Casbin中最基本、最简单的model是ACL。ACL中的model CONF为:

# Request definition
[request_definition]
r = sub, obj, act

# Policy definition
[policy_definition]
p = sub, obj, act

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

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

ACL model的示例policy如下:

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

这表示:

  • alice可以读取data1
  • bob可以编写data2

Model存储

  • 从 .CONF 文件中加载 model
e := casbin.NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")

  • 从代码加载 model
// Initialize the model from Go code.
m := casbin.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.
// 使用自己的 adapter 替换。
a := persist.NewFileAdapter("examples/rbac_policy.csv")

// 创建一个 enforcer。
e := casbin.NewEnforcer(m, a)

  • 从字符串加载的 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 = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
`
m := NewModel(text)

// Load the policy rules from the .CSV file adapter.
// Replace it with your adapter to avoid files.
a := persist.NewFileAdapter("examples/rbac_policy.csv")

// Create the enforcer.
e := casbin.NewEnforcer(m, a)

这段代码是使用Casbin库定义了一个基本的权限访问控制模型。该模型包括了以下几个部分:

  • 请求定义(r):定义了一个请求的三个要素,即主体(sub)、对象(obj)和动作(act)。
  • 策略定义(p):定义了一个策略的三个要素,即主体(sub)、对象(obj)和动作(act)。
  • 角色定义(g):定义了一种角色的概念,用于将主体与策略进行关联。
  • 策略效果(e):定义了策略的生效效果,即只要有一条策略允许访问,则允许访问。
  • 匹配器(m):定义了策略匹配的规则,即主体必须与策略的主体相同,对象必须满足keyMatch2规则,动作必须与策略的动作相同。

通过使用这个模型,可以根据主体、对象和动作来决定是否允许访问某个资源。具体的策略可以通过添加相应的规则来定义。

keyMatch规则

keyMatch是Casbin库中的一种匹配规则,用于匹配对象(obj)是否符合策略中的对象(p.obj)。

keyMatch规则支持两种通配符:

  • *:匹配任意长度的字符串,但不包括路径分隔符/
  • **:匹配任意长度的字符串,包括路径分隔符/

keyMatch规则的匹配逻辑如下:

  • 如果p.obj和r.obj相等,则匹配成功。
  • 如果p.obj是以*结尾的字符串,且r.obj以p.obj去掉最后一个*后缀的字符串开头,则匹配成功。
  • 如果p.obj是以**结尾的字符串,且r.obj包含p.obj去掉最后一个**后缀的字符串,则匹配成功。

举例来说,假设策略中的对象为/data/*,则以下对象都会匹配成功:

  • /data/file1
  • /data/dir/file2
  • /data/dir/subdir/file3

而以下对象则不会匹配成功:

  • /data
  • /datafile1
  • /datafile2

通过使用keyMatch规则,可以灵活地定义对象匹配的策略,从而实现更精细的权限控制。

具体方法参照官方文档

加载策略

要加载策略(p),你需要使用Casbin库提供的适配器(Adapter)来读取策略数据并将其加载到Casbin的模型(Model)中。

适配器(Adapter)负责将策略存储在不同的持久化存储中(如数据库、文件等),并提供读取和写入策略的接口。Casbin库提供了一些内置的适配器,如FileAdapter、MySQLAdapter等,你可以根据实际情况选择适合的适配器。

以下是一个加载策略的示例代码:

from casbin import CasbinEnforcer
from casbin.adapters import FileAdapter

# 创建一个CasbinEnforcer对象,并指定模型文件和策略适配器
enforcer = CasbinEnforcer('path/to/model.conf', 'path/to/policy.csv')

# 创建一个FileAdapter对象,并将其加载到CasbinEnforcer中
adapter = FileAdapter('path/to/policy.csv')
enforcer.set_adapter(adapter)

# 从适配器中加载策略到模型中
enforcer.load_policy()

# 现在策略已经加载到了模型中,可以进行权限访问控制了

在上述示例中,我们首先创建了一个CasbinEnforcer对象,并指定了模型文件和策略文件的路径。然后,我们创建一个FileAdapter对象,并将其加载到CasbinEnforcer中。最后,通过调用enforcer.load_policy()方法,从适配器中加载策略数据到模型中。

根据实际情况,你可以选择使用不同的适配器和加载策略的方式。请根据你的具体需求和环境进行相应的调整。

Casbin API

//全局变量 e是执行者实例
e := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")

//获取当前策略中显示的主题列表
allSubjects := e.GetAllSubjects()

//获取当前命名策略中显示的主题列表
allNamedSubjects := e.GetAllNamedSubjects("p")

//获取当前策略中显示的对象列表
allObjects := e.GetAllObjects()

//获取当前命名策略中显示的对象列表
allNamedObjects := e.GetAllNamedObjects("p")

//获取当前策略中显示的操作列表
allActions := e.GetAllActions()

//获取当前命名策略中显示的操作列表
allNamedActions := e.GetAllNamedActions("p")

//获取当前策略中显示的角色列表
allRoles = e.GetAllRoles()

//获取当前命名策略中显示的角色列表
allNamedRoles := e.GetAllNamedRoles("g")

//获取策略中的所有授权规则
policy = e.GetPolicy()

//获取策略中的所有授权规则,可以指定字段筛选器
filteredPolicy := e.GetFilteredPolicy(0, "alice")

//获取命名策略中的所有授权规则
namedPolicy := e.GetNamedPolicy("p")

//获取命名策略中的所有授权规则,可以指定字段过滤器
filteredNamedPolicy = e.GetFilteredNamedPolicy("p", 0, "bob")

//获取策略中的所有角色继承规则
groupingPolicy := e.GetGroupingPolicy()

//获取策略中的所有角色继承规则,可以指定字段筛选器
filteredGroupingPolicy := e.GetFilteredGroupingPolicy(0, "alice")

//获取策略中的所有角色继承规则
namedGroupingPolicy := e.GetNamedGroupingPolicy("g")

//获取策略中的所有角色继承规则
namedGroupingPolicy := e.GetFilteredNamedGroupingPolicy("g", 0, "alice")

// 确定是否存在授权规则
hasPolicy := e.HasPolicy("data2_admin", "data2", "read")

//确定是否存在命名授权规则
hasNamedPolicy := e.HasNamedPolicy("p", "data2_admin", "data2", "read")

//向当前策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddPolicy("eve", "data3", "read")

// 向当前命名策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddNamedPolicy("p", "eve", "data3", "read")

// 从当前策略中删除授权规则
removed := e.RemovePolicy("alice", "data1", "read")

// 移除当前策略中的授权规则,可以指定字段筛选器。 RemovePolicy 从当前策略中删除授权规则
removed := e.RemoveFilteredPolicy(0, "alice", "data1", "read")

//从当前命名策略中删除授权规则
removed := e.RemoveNamedPolicy("p", "alice", "data1", "read")

//从当前命名策略中移除授权规则,可以指定字段筛选器
removed := e.RemoveFilteredNamedPolicy("p", 0, "alice", "data1", "read")

//确定是否存在角色继承规则
has := e.HasGroupingPolicy("alice", "data2_admin")

//确定是否存在命名角色继承规则
has := e.HasNamedGroupingPolicy("g", "alice", "data2_admin")

// 向当前策略添加角色继承规则。 如果规则已经存在,函数返回false,并且不会添加规则。 如果规则已经存在,函数返回false,并且不会添加规则
added := e.AddGroupingPolicy("group1", "data2_admin")

//将命名角色继承规则添加到当前策略。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddNamedGroupingPolicy("g", "group1", "data2_admin")

// 从当前策略中删除角色继承规则
removed := e.RemoveGroupingPolicy("alice", "data2_admin")

//从当前策略中移除角色继承规则,可以指定字段筛选器
removed := e.RemoveFilteredGroupingPolicy(0, "alice")

//从当前命名策略中移除角色继承规则
removed := e.RemoveNamedGroupingPolicy("g", "alice")

//当前命名策略中移除角色继承规则,可以指定字段筛选器
removed := e.RemoveFilteredNamedGroupingPolicy("g", 0, "alice")

//添加自定义函数
func CustomFunction(key1 string, key2 string) bool {
    if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data/:resource" {
        return true
    } else if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data2/:id/using/:resId" {
        return true
    } else {
        return false
    }
}

func CustomFunctionWrapper(args ...interface{}) (interface{}, error) {
    key1 := args[0].(string)
    key2 := args[1].(string)

    return bool(CustomFunction(key1, key2)), nil
}

e.AddFunction("keyMatchCustom", CustomFunctionWrapper)

2

评论区