与你相识
博主介绍:
– 本人是普通大学生一枚,每天钻研计算机技能,CSDN主要分享一些技术内容,因我常常去寻找资料,不经常能找到合适的,精品的,全面的内容,导致我花费了大量的时间,所以会将摸索的内容全面细致记录下来。另外,我更多关于管理,生活的思考会在简书中发布,如果你想了解我对生活有哪些反思,探索,以及对管理或为人处世经验的总结,我也欢迎你来找我。
– 目前的学习专注于Go语言,辅学算法,前端领域。也会分享一些校内课程的学习,例如数据结构,计算机组成原理等等,如果你喜欢我的风格,请关注我,我们一起成长。
Table of Contents
- casbin
-
- 官方文档
-
- 基础知识
-
- 安装及简单实用
- 工作原理
- Model
-
- 支持的Model
- Model语法
-
- Request定义
- Policy定义
- Policy effect定义
- Matchers
-
- 内置函数
- RBAC
- ABAC
- 每日一库——casbin
-
- ACL模型
- RBAC模型
-
- 多个RBAC
- 多层角色
- RBAC domain
- ABAC模型
- 模型存储
- 策略存储
-
- 使用函数
- 参考资料
casbin
权限管理几乎在每个系统中都是必备的模块。如果项目开发每次都要实现一次权限管理,会浪费很多的时间,而casbin就来做这个事情,支持常用的多种访问控制模型,如
ACL/RBAC/ABAC
等。可以实现灵活的访问权限控制。
比如说什么角色,什么用户,可以通过某个api,就可以使用casbin来进行限制。
官方文档
由于官方文档到中间靠后的部分有点难以理解,所以就转向其它教程了。
基础知识
安装及简单实用
go get github.com/casbin/casbin
另外创建一个Casbin决策器需要有一个模型文件和策略文件作为参数:
import "github.com/casbin/casbin"
e := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")
然后可以在访问发生之前,给代码添加强制挂钩:
sub := "alice" // 想要访问资源的用户。
obj := "data1" //要访问的资源。
act := "read" // 用户对资源执行的操作。
if e.Enforce(sub, obj, act) == true {
// 允许alice读取data1
} else {
// 拒绝请求,显示错误
}
工作原理
casbin的访问控制模型被抽象为基于PERM(Policy(策略), Effect(效果), Request(请求), Matcher(匹配器))的一个文件。
Casbin中最基本,最简单的model是
ACL
。
ACL
中的
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
// 对于过长的单元配置,也可以通过在结尾处添加“/”进行断行
m = r.sub == p.sub && r.obj == p.obj \
&& r.act == p.act
policy
如下:
// 这表示alice可以读取data1
p, alice, data1, read
// 这表示bob可以编写data2
p, bob, data2, write
Model
支持的Model
- ACL (Access Control List, 访问控制列表)
- 具有超级用户的 ACL
- 没有用户的 ACL: 对于没有身份验证或用户登录的系统尤其有用。
- 没有资源的 ACL: 某些场景可能只针对资源的类型, 而不是单个资源, 诸如
,write-article
等权限。 它不控制对特定文章或日志的访问。read-log
- RBAC (基于角色的访问控制)
- 支持资源角色的RBAC: 用户和资源可以同时具有角色 (或组)。
- 支持域/租户的RBAC: 用户可以为不同的域/租户设置不同的角色集。
- ABAC (基于属性的访问控制): 支持利用
这种语法糖获取元素的属性。resource.Owner
- RESTful: 支持路径, 如
,/res/*
和 HTTP 方法, 如/res/: id
,GET
,POST
,PUT
。DELETE
- 拒绝优先: 支持允许和拒绝授权, 拒绝优先于允许。
- 优先级: 策略规则按照先后次序确定优先级,类似于防火墙规则。
Model语法
- Model CONF 至少应包含四个部分:
。[request_definition], [policy_definition], [policy_effect], [matchers]
- 如果 model 使用 RBAC, 还需要添加
部分。[role_definition]
Request定义
[request_definition]
部分用于request的定义,它明确了
e.Enforce(...)
函数中参数的含义。
[request_definition]
r = sub, obj, act
sub, obj, act
表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)。 但是, 你可以自定义你自己的请求表单, 如果不需要指定特定资源,则可以这样定义
sub、act
,或者如果有两个访问实体, 则为
sub、sub2、obj、act
。
Policy定义
[policy_definition]
部分是对policy的定义,以下文的 model 配置为例:
[policy_definition]
p = sub, obj, act
这些是我们对policy规则的具体描述
p, alice, data1, read
policy部分的每一行称之为一个策略规则。 上面的policy的绑定关系将会在matcher中使用, 罗列如下:
Policy effect定义
[policy_effect]
部分是对policy生效范围的定义, 原语定义了当多个policy rule同时匹配访问请求request时,该如何对多个决策结果进行集成以实现统一决策。 以下示例展示了一个只有一条规则生效,其余都被拒绝的情况:
[policy_effect]
e = some(where (p.eft == allow))
该Effect原语表示如果存在任意一个决策结果为
allow
的匹配规则,则最终决策结果为
allow
。 其中
p.eft
表示策略规则的决策结果,可以为
allow
或者
deny
,当不指定规则的决策结果时,取默认值
allow
。 通常情况下,policy的
p.eft
默认为
allow
, 因此前面例子中都使用了这个默认值。
这是另一个policy effect的例子:
[policy_effect]
e = !some(where (p.eft == deny))
该Effect原语表示不存在任何决策结果为
deny
的匹配规则,则最终决策结果为
allow
,即deny-override。
some
量词判断是否存在一条策略规则满足匹配器。另外
any
量词则判断是否所有的策略规则都满足匹配器。 policy effect还可以利用逻辑运算符进行连接:
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
该Effect原语表示当至少存在一个决策结果为
allow
的匹配规则,且不存在决策结果为
deny
的匹配规则时,则最终决策结果为
allow
。 这时
allow
授权和
deny
授权同时存在,但是
deny
优先。
Matchers
[matchers]
原语定义了策略规则如何与访问请求进行匹配的匹配器,其本质上是布尔表达式,可以理解为Request、Policy等原语定义了关于策略和请求的变量,然后将这些变量代入Matcher原语中求值,从而进行策略决策。
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
这是一个简单的例子,该Matcher原语表示,访问请求request中的subject、object、action三元组应与策略规则policy rule中的subject、object、action三元组分别对应相同。
Matcher原语支持+、 -、 *、 /等算数运算符,==,、!=、 >、 <等关系运算符以及&& (与)、|| (或)、 ! (非)等逻辑运算符。
内置函数
函数 | 释义 | 示例 |
---|---|---|
keyMatch(arg1, arg2) | 参数 arg1 是一个 URL 路径,例如 ,参数 arg2 可以是URL路径或者是一个 模式,例如 。此函数返回 arg1是否与 arg2 匹配。 | keymatch_model.conf/keymatch_policy.csv |
keyMatch2(arg1, arg2) | 参数 arg1 是一个 URL 路径,例如 ,参数 arg2 可以是 URL 路径或者是一个 模式,例如 。此函数返回 arg1 是否与 arg2 匹配。 | keymatch2_model.conf/keymatch2_policy.csv |
regexMatch(arg1, arg2) | arg1 可以是任何字符串。arg2 是一个正则表达式。它返回 arg1 是否匹配 arg2。 | keymatch_model.conf/keymatch_policy.csv |
ipMatch(arg1, arg2) | arg1 是一个 IP 地址, 如 。arg2 可以是 IP 地址或 CIDR, 如 。它返回 arg1 是否匹配 arg2。 | ipmatch_model.conf/ipmatch_policy.csv |
也可以添加自定义函数,具体参考:添加自定义函数
RBAC
看不懂
ABAC
看不懂
每日一库——casbin
ACL模型
下面可以看到我们使用ACL模型进行的权限控制
我们依然使用 Go Module 编写代码,先初始化:
$ mkdir casbin && cd casbin
$ go mod init github.com/darjun/go-daily-lib/casbin
然后安装
casbin
,目前是
v2
版本:
$ go get github.com/casbin/casbin/v2
权限实际上就是控制谁能对什么资源进行什么操作。
casbin
将访问控制模型抽象到一个基于 PERM(Policy,Effect,Request,Matchers) 元模型的配置文件(模型文件)中。因此切换或更新授权机制只需要简单地修改配置文件。
policy
是策略或者说是规则的定义。它定义了具体的规则。
request
是对访问请求的抽象,它与
e.Enforce()
函数的参数是一一对应的
matcher
匹配器会将请求与定义的每个
policy
一一匹配,生成多个匹配结果。
effect
根据对请求运用匹配器得出的所有结果进行汇总,来决定该请求是允许还是拒绝。
编写模型文件:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
[policy_effect]
e = some(where (p.eft == allow))
上面模型文件规定了权限由
sub,obj,act
三要素组成,只有在策略列表中有和它完全相同的策略时,该请求才能通过。匹配器的结果可以通过
p.eft
获取,
some(where (p.eft == allow))
表示只要有一条策略允许即可。
然后我们策略文件(即谁能对什么资源进行什么操作):
p, dajun, data1, read
p, lizi, data2, write
上面
policy.csv
文件的两行内容表示
dajun
对数据
data1
有
read
权限,
lizi
对数据
data2
有
write
权限。
接下来就是使用的代码:
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
代码其实不复杂。首先创建一个
casbin.Enforcer
对象,加载模型文件
model.conf
和策略文件
policy.csv
,调用其
Enforce
方法来检查权限。运行程序:
$ go run main.go
dajun CAN read data1
lizi CAN write data2
dajun CANNOT write data1
dajun CANNOT read data2
请求必须完全匹配某条策略才能通过。
("dajun", "data1", "read")
匹配
p, dajun, data1, read
,
("lizi", "data2", "write")
匹配
p, lizi, data2, write
,所以前两个检查通过。第 3 个因为
"dajun"
没有对
data1
的
write
权限,第 4 个因为
dajun
对
data2
没有
read
权限,所以检查都不能通过。输出结果符合预期。
sub/obj/act
依次对应传给
Enforce
方法的三个参数。实际上这里的
sub/obj/act
和
read/write/data1/data2
是我自己随便取的,你完全可以使用其它的名字,只要能前后一致即可。
上面例子中实现的就是
ACL
(access-control-list,访问控制列表)。
ACL
显示定义了每个主体对每个资源的权限情况,未定义的就没有权限。我们还可以加上超级管理员,超级管理员可以进行任何操作。假设超级管理员为
root
,我们只需要修改匹配器:
[matchers]
e = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
只要访问主体是
root
一律放行。
验证:
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "root", "data1", "read")
check(e, "root", "data2", "write")
check(e, "root", "data1", "execute")
check(e, "root", "data3", "rwx")
}
因为
sub = "root"
时,匹配器一定能通过,运行结果:
$ go run main.go
root CAN read data1
root CAN write data2
root CAN execute data1
root CAN rwx data3
RBAC模型
ACL
在用户和资源比较少的情况下没有什么问题,但是如果用户很多的话,就会显得非常的繁琐,试想一下每次新增一个用户,都要把他需要的权限重新设置一遍是多么地痛苦。
所以
RBAC
就通过引入
角色(role)
来解决这个问题,每个用户都属于一个角色,每个角色都有其特定的权限,这样新增用户的时候,只需要给他指派一个角色,他就可以拥有该角色对应的权限信息。
使用
RBAC
模型需要在
ACL
MODEL的基础上添加
role_definition
模块:
g = _,_
定义了用户——角色,角色——角色的映射关系,前者是后者的成员,拥有后者的权限。然后在匹配器中,我们不需要判断
r.sub
与
p.sub
是否相当,只需要使用
g(r.sub, p.sub)
来判断请求主体
r.sub
是否属于
p.sub
这个角色即可。
[role_definition]
g = _, _
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
最后再修改策略文件,下面的文件就规定了
dajun
属于
admin
管理员,
lizi
属于
developer
开发者,使用
g
来定义这层关系。
另外具体的角色,如
admin
对数据data有rede和write权限也通过
p
来定义。
p, admin, data, read
p, admin, data, write
p, developer, data, read
g, dajun, admin
g, lizi, developer
我们编写主程序
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data", "read")
check(e, "dajun", "data", "write")
check(e, "lizi", "data", "read")
check(e, "lizi", "data", "write")
}
结果:
dajun CAN read data
dajun CAN write data
lizi CAN read data
lizi CANNOT write data
多个RBAC
可以为用户和资源都赋予角色的概念
[role_definition]
g=_,_
g2=_,_
[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act
上面的模型文件定义了两个
RBAC
系统
g
和
g2
,我们在匹配器中使用
g(r.sub, p.sub)
判断请求主体属于特定组,
g2(r.obj, p.obj)
判断请求资源属于特定组,且操作一致即可放行。
p, admin, prod, read
p, admin, prod, write
p, admin, dev, read
p, admin, dev, write
p, developer, dev, read
p, developer, dev, write
p, developer, prod, read
g, dajun, admin
g, lizi, developer
g2, prod.data, prod
g2, dev.data, dev
先看角色关系,即最后 4 行,
dajun
属于
admin
角色,
lizi
属于
developer
角色,
prod.data
属于生产资源
prod
角色,
dev.data
属于开发资源
dev
角色。
admin
角色拥有对
prod
和
dev
类资源的读写权限,
developer
只能拥有对
dev
的读写权限和
prod
的读权限。
check(e, "dajun", "prod.data", "read")
check(e, "dajun", "prod.data", "write")
check(e, "lizi", "dev.data", "read")
check(e, "lizi", "dev.data", "write")
check(e, "lizi", "prod.data", "write")
dajun CAN read prod.data
dajun CAN write prod.data
lizi CAN read dev.data
lizi CAN write dev.data
lizi CANNOT write prod.data
多层角色
可以为角色定义所属角色,这种权限关系可以进行传递。
例如
dajun
属于高级开发者
senior
,
seinor
属于开发者,那么
dajun
也是开发者,拥有开发者的所有权限,那么我们就可以定义开发者共有的权限,然后额外为高级开发者
senior
定义一些特殊的权限。
模型文件不需要修改,策略文件改动如下:
# 定义了senior对数据data有write权限
p, senior, data, write
# 定义了developer对数据data有read权限
p, developer, data, read
# 为dajun赋予senior权限
g, dajun, senior
# 为senior赋予developer权限,senior权限本身对data有write权限,又被加了一个developer权限,就可写可读了。
g, senior, developer
# 为lizi赋予developer权限
g, lizi, developer
RBAC domain
角色可以是全局的,也可以是特定域(domain)的或租户(tenant),可以理解为
组
。
例如
dajun
在组
tenant1
中是管理员,拥有很高的权限,但是在
tenant2
中可能只是一个底层人员。
使用
RBAC domain
需要对模型文件做以下修改:
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _,_,_
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.obj
g=_,_,_
表示前者在后者中拥有中间定义的角色,在匹配器中使用
g
要带上
dom
。
# admin在tenant1中对data1有read权限
p, admin, tenant1, data1, read
# admin在tenant2中对data2有read权限
p, admin, tenant2, data2, read
# dajun在tenant1中的角色是admin
g, dajun, admin, tenant1
# dajun在tenant2中的角色是developer
g, dajun, developer, tenant2
然后我们编写主程序
func check(e *casbin.Enforcer, sub, domain, obj, act string) {
ok, _ := e.Enforce(sub, domain, obj, act)
if ok {
fmt.Printf("%s CAN %s %s in %s\n", sub, act, obj, domain)
} else {
fmt.Printf("%s CANNOT %s %s in %s\n", sub, act, obj, domain)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "tenant1", "data1", "read")
check(e, "dajun", "tenant2", "data2", "read")
}
结果不出意料:
dajun CAN read data1 in tenant1
dajun CANNOT read data2 in tenant2
ABAC模型
RBAC
模型对于实现比较规则的、相对静态的权限管理比较好用,但是例如我们需要在不同的时间段对数据
data
实现不同的权限控制,比如正常的工作时间
9:00-18:00
所有人都可以读写
data
,其它时间只有数据所有者能读写。这种需求就需要使用
ABAC
模型完成。
首先修改
model
文件:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[matchers]
m = r.sub.Hour >= 9 && r.sub.Hour < 18 || r.sub.Name == r.obj.Owner
[policy_effect]
e = some(where (p.eft == allow))
ABAC
模型不需要策略文件:
type Object struct {
Name string
Owner string
}
type Subject struct {
Name string
Hour int
}
func check(e *casbin.Enforcer, sub Subject, obj Object, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s at %d:00\n", sub.Name, act, obj.Name, sub.Hour)
} else {
fmt.Printf("%s CANNOT %s %s at %d:00\n", sub.Name, act, obj.Name, sub.Hour)
}
}
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
o := Object{"data", "dajun"}
s1 := Subject{"dajun", 10}
check(e, s1, o, "read")
s2 := Subject{"lizi", 10}
check(e, s2, o, "read")
s3 := Subject{"dajun", 20}
check(e, s3, o, "read")
s4 := Subject{"lizi", 20}
check(e, s4, o, "read")
}
dajun CAN read data at 10:00
lizi CAN read data at 10:00
dajun CAN read data at 20:00
lizi CANNOT read data at 20:00
使用
ABAC
模型可以非常灵活的进行权限控制,但是在一般情况的
RBAC
就够用了。
模型存储
casbin可以实现在代码中动态初始化模型:
func main() {
m := model.NewModel()
m.AddDef("r", "r", "sub, obj, act")
m.AddDef("p", "p", "sub, obj, act")
m.AddDef("e", "e", "some(where (p.eft == allow))")
m.AddDef("m", "m", "r.sub == g.sub && r.obj == p.obj && r.act == p.act")
a := fileadapter.NewAdapter("./policy.csv")
e, err := casbin.NewEnforcer(m, a)
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
或者在字符串中加载
func main() {
text := `
[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
`
m, _ := model.NewModelFromString(text)
a := fileadapter.NewAdapter("./policy.csv")
e, _ := casbin.NewEnforcer(m, a)
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
但是这两种方式都不推荐使用
策略存储
在前面的例子中,我们都是将策略存储在一个.csv文件中,但是实际应用中一般不用这种方式存储,
casbin
以第三方适配器的方式支持多种存储方式,包括
Mysql/MongoDB/Redis/Etcd
等。
CREATE DATABASE IF NOT EXISTS casbin;
USE casbin;
CREATE TABLE IF NOT EXISTS casbin_rule (
p_type VARCHAR(100) NOT NULL,
v0 VARCHAR(100),
v1 VARCHAR(100),
v2 VARCHAR(100),
v3 VARCHAR(100),
v4 VARCHAR(100),
v5 VARCHAR(100)
);
INSERT INTO casbin_rule VALUES
('p', 'dajun', 'data1', 'read', '', '', ''),
('p', 'lizi', 'data2', 'write', '', '', '');
然后使用
Gorm Adapter
加载
policy
,
Gorm Adapter
默认使用
casbin
库中的
casbin_rule
表:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
gormadapter "github.com/casbin/gorm-adapter/v2"
_ "github.com/go-sql-driver/mysql"
)
func check(e *casbin.Enforcer, sub, obj, act string) {
ok, _ := e.Enforce(sub, obj, act)
if ok {
fmt.Printf("%s CAN %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
}
}
func main() {
a, _ := gormadapter.NewAdapter("mysql", "root:[email protected](127.0.0.1:3306)/")
e, _ := casbin.NewEnforcer("./model.conf", a)
check(e, "dajun", "data1", "read")
check(e, "lizi", "data2", "write")
check(e, "dajun", "data1", "write")
check(e, "dajun", "data2", "read")
}
运行:
dajun CAN read data1
lizi CAN write data2
dajun CANNOT write data1
dajun CANNOT read data2
使用函数
我们可以在匹配器中使用函数。
casbin
内置了一些函数
keyMatch/keyMatch2/keyMatch3/keyMatch4
都是匹配 URL 路径的,
regexMatch
使用正则匹配,
ipMatch
匹配 IP 地址。参见https://casbin.org/docs/en/function。使用内置函数我们能很容易对路由进行权限划分:
[matchers]
m = r.sub == p.sub && keyMatch(r.obj, p.obj) && r.act == p.act
p, dajun, user/dajun/*, read
p, lizi, user/lizi/*, read
不同用户只能访问其对应路由下的 URL:
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "dajun", "user/dajun/1", "read")
check(e, "lizi", "user/lizi/2", "read")
check(e, "dajun", "user/lizi/1", "read")
}
输出:
dajun CAN read user/dajun/1
lizi CAN read user/lizi/2
dajun CANNOT read user/lizi/1
我们当然也可以定义自己的函数。先定义一个函数,返回 bool:
func KeyMatch(key1, 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
}
然后添加到权限认证器中:
e.AddFunction("my_func", KeyMatchFunc)
这样我们就可以在匹配器中使用该函数实现正则匹配了:
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act
接下来我们在策略文件中为
dajun
赋予权限:
p, dajun, data/*, read
dajun
对匹配模式
data/*
的文件都有
read
权限。
验证一下:
check(e, "dajun", "data/1", "read")
check(e, "dajun", "data/2", "read")
check(e, "dajun", "data/1", "write")
check(e, "dajun", "mydata", "read")
dajun
对
data/1
没有
write
权限,
mydata
不符合
data/*
模式,也没有
read
权限:
dajun CAN read data/1
dajun CAN read data/2
dajun CANNOT write data/1
dajun CANNOT read mydata
参考资料
- casbin中文开发文档(官方)
- 每日一库——casbin
欢迎评论区讨论,或指出问题。 如果觉得写的不错,欢迎点赞,转发,收藏。