简介
Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。
Casbin提供了一个执行者 根据提供给执行者的策略和模型文件验证传入的请求。再根据对应的配置授权策略,验证请求判断释放那些行动。
在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个配置文件。PERM模式由四个基础(策略、效果、请求、匹配)组成,描述了资源与用户之间的关系。通过配置文件对用户授权更方便授权系统的更改和迭代。
Casbin使用配置文件来设置访问控制模式。
工作原理
访问控制模型PERM是casbin的权限机制,通过4个对象Policy, Effect, Request, Matcher描述了资源的关系。
请求
定义请求参数。 基本请求是一个元组对象,至少需要访问实体(Subject)、访问资源(Object) 和访问方式(Action)。
请求是访问携带的对象,携带用户访问权限。
r = sub, obj, act
策略
policy部分的每一行称之为一个策略规则, 每条策略规则通常以形如p, p2的policy type开头。策略定义了匹配模式。不同的模式需要满足不同的匹配规则。
p = sub, obj, act
p2 = sub, act
规则
匹配请求和匹配策略的比较规则,不同的匹配策略比较方式不同。
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
在策略中定义了p和p2两种匹配模式,匹配规则中定义了请求和p策略的比较规则。
效果
效果是匹配结果的判断。
e = some (where (p.eft == allow))
匹配策略p没有定义匹配结果eft,但是每个匹配规则都会默认存在匹配结果。
RBACABAC
一个完整的ACL模型如下:
# casbin请求格式
[request_definition]
r = sub, obj, act# casbin策略模式
[policy_definition]
p = sub, obj, act
p2 = sub, act# casbin测率匹配结果
[policy_effect]
e = some(where (p.eft == allow))# casbin率略匹配规则
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
[request_definition]请求
上述的ACL模型表示的含义是定义了sub, obj, act为匹配对象的p策略;定义请求p需要按照策略的格式携带参数;定义匹配规则m对请求参数匹配请求策略;最好返回匹配结果e。
这些过程都是casbin框架自主完成的,只需要使用casbin提供的接口就可以按规则传入参数并得到匹配结果。但是访问权限控制的这些规则需要定义,因为官方提供了众多规则模型,需要开发者自己选择合适的模型。
model.conf
# casbin请求格式
[request_definition]
r = sub, obj, act# casbin策略模式
[policy_definition]
p = sub, obj, act
p2 = sub, act# casbin测率匹配结果
[policy_effect]
e = some(where (p.eft == allow))# casbin率略匹配规则
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
策略授权
访问权限控制模型已经定义了,就需要定义具体的用户授权了,该步骤是将实际的用户与匹配规则联系起来。
police.csv
p,xiaoxu,data1,read
p,zhangsan,data2,write
上述匹配规则下p模式,xiaoxu用户可以访问,data1,访问限制为read;zhangsan用户可以访问data2,访问权限为write。
案例
简单案例
主函数如下:
package mainimport ("fmt""github.com/casbin/casbin/v2"
)func main() {e, err := casbin.NewEnforcer("model.conf", "policy.csv")if err != nil {println(err)return}enforce, err := e.Enforce("xiaoxu", "data1", "read")if err != nil {println(err)}fmt.Println("访问权限结果", enforce)}
casbin.NewEnforcer()casbin.Enforcere.Enforce
main函数的执行结果为true,表示"xiaoxu", “data1”, "read"满足匹配策略有访问权限。
如果将用户名修改为在policy.csv没有相应的配置,就会返回false表示没有权限访问。
gin框架中间件整合casbin
package mainimport ("github.com/casbin/casbin/v2""github.com/gin-gonic/gin"
)func main() {/*e, err := casbin.NewEnforcer("model.conf", "policy.csv")if err != nil {println(err)return}enforce, err := e.Enforce("xiaoxu1", "data1", "read")if err != nil {println(err)}fmt.Println("访问权限结果", enforce)*///gin路由engine := gin.Default()engine.GET("/data1", casbinAuth(), func(context *gin.Context) {context.String(200, "data1")})engine.GET("/data2", casbinAuth(), func(context *gin.Context) {context.String(200, "data2")})engine.GET("/data3", casbinAuth(), func(context *gin.Context) {context.String(200, "data3")})engine.Run("127.0.0.1:8080")
}//定义权限中间件func casbinAuth() gin.HandlerFunc {return func(context *gin.Context) {var req CasbinRequestcontext.ShouldBindJSON(&req)if casBin(req.Sub, req.Obj, req.Act) {context.Next()} else {context.Next()context.String(503, "没有权限访问!")}}
}//定义casbin模型func casBin(sub, obj, act string) bool {e, err := casbin.NewEnforcer("model.conf", "policy.csv")if err != nil {println(err)return false}enforce, err := e.Enforce(sub, obj, act)if err != nil {println(err)return false}return enforce
}type CasbinRequest struct {Sub string `json:"sub"`Obj string `json:"obj"`Act string `json:"act"`Data string `json:"data"`
}
通过gin的中间价调用casbin的方法判断是否有访问权限。
将权限配置做如下修改:
p,xiaoxu,data1,read
p,xiaoxu,data1,writep,xiaoxu,data2,writep,xiaoxu,data3,readp,zhangsan,data2,read
p,zhangsan,data3,read
在实际项目中policy.csv的配置显然是不合适的,在web项目中,权限对应的是api接口,那么就需要与当前地址匹配,判断用户权限。
casbin.NewEnforcer
package mainimport ("github.com/casbin/casbin/v2""github.com/gin-gonic/gin""strings"
)func main() {engine := gin.Default()engine.GET("/data1/list", casbinAuth(), func(context *gin.Context) {context.String(200, " data1 list ...")})engine.GET("/data1/add", casbinAuth(), func(context *gin.Context) {context.String(200, "data1 add ...")})engine.GET("/data1/delete", casbinAuth(), func(context *gin.Context) {context.String(200, "data1 delete ...")})engine.GET("/data1/update", casbinAuth(), func(context *gin.Context) {context.String(200, "data1 update ...")})engine.GET("/data2/list", casbinAuth(), func(context *gin.Context) {context.String(200, "data2 list ...")})engine.GET("/data2/add", casbinAuth(), func(context *gin.Context) {context.String(200, "data2 add ...")})engine.GET("/data2/delete", casbinAuth(), func(context *gin.Context) {context.String(200, "data2 delete ...")})engine.GET("/data2/update", casbinAuth(), func(context *gin.Context) {context.String(200, "data2 update ...")})engine.Run("127.0.0.1:8080")
}//定义权限中间件func casbinAuth() gin.HandlerFunc {return func(context *gin.Context) {var req Usernamecontext.ShouldBindJSON(&req)//获取路径地址path := context.FullPath()split := strings.Split(path, "/")//fmt.Println(split)sub := req.Usernameobj := split[1] //请求的资源act := split[2] //请求资源的权限//if casBin(sub, obj, act) {context.Next()} else {context.Next()context.String(503, "没有权限访问!")}}
}//定义casbin模型func casBin(sub, obj, act string) bool {e, err := casbin.NewEnforcer("model.conf", "policy.csv")if err != nil {println(err)return false}enforce, err := e.Enforce(sub, obj, act)if err != nil {println(err)return false}return enforce
}type Username struct {Username string `json:"username"`
}
policy.csv如下:
p,xiaoxu,data1,list
p,xiaoxu,data1,add
p,xiaoxu,data1,delete
p,xiaoxu,data1,updatep,zhangsan,data1,list
p,zhangsan,data1,addp,list,data2,list
p,list,data2,add
p,list,data2,delete
p,list,data2,update
data1类似的配置是配置资源,add等表示增删改查的行动,也表示uil地址。上述配置赋予了xiaoxu的增删改查的全部权限,zhansan用户只有查和增,list有data2资源的全部权限。
重构代码如下:
用户权限的配置不仅限于policy.csv文件配置,也可以使用数据库。
model.conf
更改适配器官方文档
casbin官方文档
教程文档