casbin是一个强大、高效的访问控制库。支持常用的多种访问控制模型,如ACL/RBAC/ABAC等。可以实现灵活的访问权限控制。同时,casbin支持多种编程语言,Go/Java/Node/PHP/Python/.NET/Rust。本文以Go作为示例进行描述。
先来看第一个例子,在这个例子中,我们控制用户名为“admin”的用户对web路径“/user/*”,有“get”的访问权限,用户“user”对web路径“/goods/list”有“post”访问权限,用户“root”对一切都有访问权限。
require github.com/casbin/casbin v1.9.1
[request_definition]
r = user, path, method
[policy_definition]
p = user, path, method
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.user == p.user && keyMatch(r.path , p.path) && r.method == p.method || r.user == "root"
p, admin, /user/*, get
p, user, /goods/list, post
package main
import (
"github.com/casbin/casbin"
"log"
)
func main() {
e:= casbin.NewEnforcer("./abac_model.conf","./abac_policy.csv")
enforce := e.Enforce("a", "/user/list", "get")
log.Printf("\"a\", \"/user/list\", \"get\"->%v\n",enforce)
enforce = e.Enforce("a", "/user/add", "get")
log.Printf("\"a\", \"/user/add\", \"get\"->%v\n",enforce)
enforce = e.Enforce("admin", "/user/add", "get")
log.Printf("\"admin\", \"/user/add\", \"get\"->%v\n",enforce)
enforce = e.Enforce("admin", "/user/add", "get")
log.Printf("\"admin\", \"/user/add\", \"get\"->%v\n",enforce)
enforce = e.Enforce("admin", "/user/list", "get")
log.Printf("\"admin\", \"/user/list\", \"get\"->%v\n",enforce)
enforce = e.Enforce("admin", "/user/add", "post")
log.Printf("\"admin\", \"/user/add\", \"post\"->%v\n",enforce)
enforce = e.Enforce("admin", "/goods/list", "post")
log.Printf("\"admin\", \"/goods/list\", \"post\"->%v\n",enforce)
enforce = e.Enforce("user", "/user/add", "get")
log.Printf("\"user\", \"/user/add\", \"get\"->%v\n",enforce)
enforce = e.Enforce("user", "goods/list", "get")
log.Printf("\"user\", \"goods/list\", \"get\"->%v\n",enforce)
enforce = e.Enforce("user", "/goods/list", "post")
log.Printf("\"user\", \"/goods/list\", \"post\"->%v\n",enforce)
enforce = e.Enforce("root", "/user/add", "get")
log.Printf("\"root\", \"/user/add\", \"get\"->%v\n",enforce)
enforce = e.Enforce("root", "goods/list", "get")
log.Printf("\"root\", \"goods/list\", \"get\"->%v\n",enforce)
enforce = e.Enforce("root", "/goods/list", "post")
log.Printf("\"root\", \"/goods/list\", \"post\"->%v\n",enforce)
enforce = e.Enforce("root", "/aa/add", "post")
log.Printf("\"root\", \"/aa/add\", \"post\"->%v\n",enforce)
}
2021/05/30 17:42:09 "a", "/user/list", "get"->false
2021/05/30 17:42:09 "a", "/user/add", "get"->false
2021/05/30 17:42:09 "admin", "/user/add", "get"->true
2021/05/30 17:42:09 "admin", "/user/add", "get"->true
2021/05/30 17:42:09 "admin", "/user/list", "get"->true
2021/05/30 17:42:09 "admin", "/user/add", "post"->false
2021/05/30 17:42:09 "admin", "/goods/list", "post"->false
2021/05/30 17:42:09 "user", "/user/add", "get"->false
2021/05/30 17:42:09 "user", "goods/list", "get"->false
2021/05/30 17:42:09 "user", "/goods/list", "post"->true
2021/05/30 17:42:09 "root", "/user/add", "get"->true
2021/05/30 17:42:09 "root", "goods/list", "get"->true
2021/05/30 17:42:09 "root", "/goods/list", "post"->true
2021/05/30 17:42:09 "root", "/aa/add", "post"->true
其实casbin的使用非常简单
第一步,初始化一个Enforcer:
e:= casbin.NewEnforcer("./abac_model.conf","./abac_policy.csv")
第二步,对输入参数进行验证,例如:
enforce := e.Enforce(“a”, “/user/list”, “get”)
通过观察输出结果,我们可以看到,我们实现了我们要达到的访问控制效果。那么整个过程是怎么实现的呢,下面我们来详细说明下。
首先我们来看访问控制模型(abac_model.conf),在这个文件中,我们定义了访问控制的规则:
在模型中, [request_definition]后面一行就是请求定义,它定义了我们的输入参数,也就是方法e.Enforce(“a”, “/user/list”, “get”),各个参数的意义,比如在上面的例子中,第一个参数是用户名,第二个参数是web访问路径,第三个参数是访问方法。需要注意的是,在进行验证的时候,传入参数一定要和模型中定义的一致。
策略定义是以 [policy_definition]开始的,其后面的内容就是策略定义。这里定义了每条策略各个子项的含义。在上面的例子中,它定义了一条策略中,第一项是用户名,第二项是web访问路径,第三项是访问方法。在后面制定策略的时候就按照这个定义来做,比如上面的例子了,可以看到策略文件中,p后面分别是用户名,web访问路径,访问方法。
策略效果以[policy_effect]开始,是用来说明,各个匹配结果怎样对最终结果产生影响,比如上面例子中的策略效果:e = some(where (p.eft == allow)),它表示只要有一条策略记录和请求参数匹配了,那么最终结果就是allow。
匹配器以[matchers]开始,它说明了,输入参数和策略记录怎样进行匹配,比如上面的例子中(m = r.user == p.user && keyMatch(r.path , p.path) && r.method == p.method || r.user == “root”),它的意思是,输入参数的用户名和策略记录中的用户名要相当,并且输入参数和策略记录中的web访问路径要匹配,并且输入参数和策略记录中的方法字段要相当,同时满足这3个条件则输入参数和策略记录是匹配的,结果是allow的;或者符号“||”,说明前面3个条件没有同时满足,但是输入请求用户名是root,那么输入请求就是和策略记录匹配的,结果是allow的。特别说明一下,匹配器里面可以使用函数,比如上面例子中的keyMatch,也可以自定义函数,细节可以参考官方文档,这里不详述。
策略记录是记录了,访问策略的记录,它可以用文件进行保存,也可以保存到数据,通过不同adapter的来实现。细节请参考官方文档关于adapter的叙述。
通过例子1的分析,我们可以发现,casbin的规则定义是比较灵活的,要验证什么内容,怎样验证,我们都可以自定义的。我们再来看一个例子,我们现在有一个功能只能ip地址为192.168开头的用户才能访问。
[request_definition]
r = act, ip
[policy_definition]
p = act, ip
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.act == p.act&& keyMatch(r.ip, p.ip)
p, admin, 192.168*
package main
import (
"github.com/casbin/casbin"
"log"
)
func main() {
e:= casbin.NewEnforcer("./abac_model.conf","./abac_policy.csv")
enforce := e.Enforce("admin", "192.168.0.11")
log.Printf("\"admin\", \"192.168.0.11\"->%v\n",enforce)
enforce = e.Enforce("admin", "192.167.0.11")
log.Printf("\"admin\", \"192.167.0.11\"->%v\n",enforce)
enforce = e.Enforce("manage", "192.168.0.11")
log.Printf("\"manage\", \"192.168.0.11\"->%v\n",enforce)
}
2021/05/30 20:30:23 "admin", "192.168.0.11"->true
2021/05/30 20:30:23 "admin", "192.167.0.11"->false
2021/05/30 20:30:23 "manage", "192.168.0.11"->false
可以看到,只有第一个请求是allow的,第二个是因为ip地址不匹配,第三个是功能不匹配。
在权限控制里面,我们常常有这样的需求,那就是我们希望我们可以定义一些角色,给这些角色赋予某些权限,然后给用户指定某些角色,从而使用户具有角色所拥有的所有权限。要达到这个效果,在casbin里面要怎么去做呢?还是来看看下面的例子吧。
[request_definition]
r = user, path, method
[policy_definition]
p = role, path, method
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.user, p.role) && keyMatch(r.path , p.path) && r.method == p.method
p, manager, /user/*, get
p, seller, /goods/list, post
g, admin, manager
g, admin, seller
g, user1, seller
admin,/user/list,get -->true
admin,/goods/list,post -->true
user1,/user/list,get -->false
user1,/goods/list,post -->true
通过上面的例子,我们可以看到,我们在控制模型中加上[role_definition],在匹配器中加上g(r.user, p.role)就能达到我们引入角色的目的。其实,g(r.user, p.role)的作用就是从请求参数中拿出user参数,从策略记录中拿出role字段,然后和分组记录(访问策略文件中g开头的记录)进行匹配,如果匹配到了g(r.user, p.role)的结果就是true。
由于本人也是刚接触casbin,有什么阐述得不对的,欢迎留言指正。另外,casbin的官网地址如下:casbin官网;官网提供的编辑器,可以用来验证策略记录是否正确:https://casbin.org/en/editor/