casbin
将访问控制模型抽象到一个基于 PERM(Policy,Effect,Request,Matchers) 元模型的配置文件(模型文件)中
policy
是策略或者说是规则的定义。它定义了具体的规则。
request
是对访问请求的抽象,它与e.Enforce()
函数的参数是一一对应的
matcher
匹配器会将请求与定义的每个policy
一一匹配,生成多个匹配结果。
effect
根据对请求运用匹配器得出的所有结果进行汇总,来决定该请求是允许还是拒绝。
下载依赖
go get github.com/casbin/casbin/v2
# 请求定义
[request_definiation]
r = sub,obj,act
# sub ——> 想要访问资源的用户角色(Subject)——请求实体
# obj ——> 访问的资源(Object)
# act ——> 访问的方法(Action: get、post...)
# 策略定义
# 策略(.csv文件p的格式,定义的每一行为policy rule;p,p2为policy rule的名字。)
[policy_definiation]
p = sub,obj,act
# p2 = sub,act 表示sub对所有资源都能执行act
# 组定义
[role_definiation]
g = _, _
# g = _,_定义了用户——角色,角色——角色的映射关系,前者是后者的成员,拥有后者的权限。
# _,_表示用户,角色/用户组
# 策略效果
[policy_effect]
e = some(where(p.eft = allow))
# 上面表示有任意一条 policy rule 满足, 则最终结果为 allow;p.eft它可以是allow或deny,它是可选的,默认是allow
# 匹配器
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
# 上面模型文件规定了权限由sub,obj,act三要素组成,只有在策略列表中有和它完全相同的策略时,该请求才能通过。
p,zxp,data1,read
p,zhang,data2,write
package main
import (
"fmt"
"log"
"github.com/casbin/casbin/v2"
)
func check(e *casbin.Enforcer,sub,obj,act string){
ok,_ := e.Enforcer(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() {
//首先创建一个casbin.Enforcer对象,加载模型文件model.conf和策略文件policy.csv,调用其 Enforce方法来检查权限
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
log.Fatalf("NewEnforecer failed:%v\n", err)
}
check(e, "zxp", "data1", "read")
check(e, "zhang", "data2", "write")
check(e, "zxp", "data1", "write")
check(e, "zxp", "data2", "read")
测试结果:
zxp CAN read data1
zhang CAN write data2
zxp CANNOT write data1
zxp CANNOT read data2
[matchers]
e = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
g = _,__定义了用户——角色或角色——角色的映射关系,前者是后者的成员,拥有后者的权限。
model文件配置为
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
policy文件配置为
p, admin, data, read
p, admin, data, write
p, developer, data, read
g, zxp, admin
g, zhang, developer
admin
对数据data
用read
和write
权限,而developer
对数据data
只有read
权限。
zxp
拥有admin的权限所以可读可写 zhang
只能读
model文件配置改为:
[role_definition]
g=_,_
g2=_,_
[matchers]
m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act
# 只改这两个,其他不变
policy文件举例如下:
p, admin, data1, read
p, admin, data1, write
p, admin, data2, read
p, admin, data2, write
p, developer, data2, read
p, developer, data2, write
p, developer, data1, read
g, zxp, admin
g, zhang, developer
g2, data1.data, data1
g2, data2.data, data2
zxp
属于admin
所以对data1
,data2
可读可写
zhang
属于developer
所以对data1
可读。对data2
可读可写
data1.data
属于data1
,所以zxp
可读可写,zhang
对data1.data
可读
data2.data
属于data2
,所以zhang
可读可写
model文件不用修改
policy文件举例如下:
p, senior, data, write
p, developer, data, read
g, zxp, senior
g, senior, developer
g, zhang, developer
senior
对data
有write
权限
developer
只有read
权限,同时senior
也是developer
(前者拥有后者的权限),所以senior
也继承其read
权限。
zxp
可读可写data
zhang
可读data
model文件修改如下
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _,_,_
# g2 = _,_,_ 表示用户, 角色/用户组, 域(也就是租户)
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
policy文件举例如下:
p, admin, dom1, data1, read
p, admin, dom2, data2, read
g, zxp, admin, dom1
在dom1
中,只有admin
可以读取数据data1
。
在dom2
中,只有admin
可以读取数据data2
。
zxp
在data1
中是admin
,但是在data2
中不是。
代码实现
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", "dom1", "data1", "read")
check(e, "dajun", "dom2", "data2", "read")
}
model文件修改如下
[matchers]
m = r.sub.Hour >= 5 && r.sub.Hour < 20 || r.sub.Name == r.obj.Owner>
注意:该模式下不需要policy文件配置
代码实现
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)
}
//r.sub.Hour < 18 || r.sub.Name == r.obj.Owner 这两个满足一个就可读
o := Object{"data", "zxp"}
s1 := Subject{"zxp", 10}
check(e, s1, o, "read")//可读
s2 := Subject{"zhang", 10}
check(e, s2, o, "read")//可读
s4 := Subject{"zhang", 20}
check(e, s4, o, "read")//不可读
func check(e *casbin.Enforcer,sub,obj,act string){
ok,_ := e.Enforcer(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() {
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, "zxp", "data1", "read")
check(e, "zhang", "data2", "write")
check(e, "zxp", "data1", "write")
check(e, "zxp", "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)
}
使用Gorm Adapter
。先连接到数据库,执行下面的SQL
:
CREATE DATABASE IF NOT EXISTS casbin;
USE casbin;
CREATE TABLE IF NOT EXISTS casbin_rule (
p_type VARCHAR(100) NOT NULL,
s0 VARCHAR(100),
s1 VARCHAR(100),
s2 VARCHAR(100)
);
INSERT INTO casbin_rule VALUES
('p', zxp', 'data1', 'read', '', '', ''),
('p', 'zhang', 'data2', 'write', '', '', '');
代码实现
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:12345@tcp(127.0.0.1:3306)/")
e, _ := casbin.NewEnforcer("./model.conf", a)
check(e, "zxp", "data1", "read")//可读
check(e, "zhang", "data2", "write")//可写
}