一、什么是casbin
Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。因此Casbin不能做身份验证, 最佳的实践是只负责访问控制。
稍微大一点的项目都会用到鉴权机制,之前都是手写的表规则,这次casbin提供全新的思路,而且支撑多种语言适配。刚好最近在使用gin做项目,打算用casbin做权限管理,下面跟大家分享一下。
二、casbin的模型:
Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件,这个文件的具体呈现是一个以 .conf 作为后缀的文件:
#请求资源
[request_definition]
r = sub, obj, act
#策略定义
[policy_definition]
p = sub, obj, act
#Effect
[policy_effect]
e = some(where (p.eft == allow))
#匹配规则
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
#模型是官方的规则,已经写好了,基于这个模型我们可以添加一下规则文件,等看完我们就大概知道他的设计思路,现在应该还是一脸懵逼,下面是我们编写的规则文件:
p, admin, /posts, POST
p, xiaoluo, /posts/:id, PUT
p, test, /posts/:id, DELETE
我们定义了用户:
1、admin 的用户可以访问/posts 的资源并且使用的是POST的方法。
2、xiaoluo 的用户可以访问/posts/:id 的资源并且使用的是PUT的方法
3、test 的用户可以访问/posts/:id的资源并且使用的是DELETE方法
#到这里是不是跟我们在写用户权限管理的时候是一样的,下面我们在goland 编辑器下使用项目大家测试下
三、创建一个casbin-project项目,代码结构如下:
1、我们看下main文件:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
sub := ""
obj := "/posts"
act := "GET"
e, err := casbin.NewEnforcer("resources/model.conf", "resources/policy.csv")
checkError(err)
ok, err := e.Enforce(sub, obj, act)
checkError(err)
if ok {
fmt.Println("通过!")
} else {
fmt.Println("不通过!")
}
}
// 统一错误检查
func checkError(err error) {
if err != nil {
println(err.Error())
}
}
2、看下模型文件: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
3、看下我们编写的规则
p, admin, /posts, POST
p, xiaoluo, /posts/:id, PUT
p, test, /posts/:id, DELETE
4、执行结果是不通过:
为什么呢?我们可以看一下我们main文件对应的三个指标:
sub := "admin"
obj := "/posts"
act := "GET"
但是我们规则里面只是允许admin 这个用户可以访问/posts 的post方法。我们把上面act 的GET 改成POST 是就可以通过了:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
sub := "admin"
obj := "/posts"
act := "POST"
e, err := casbin.NewEnforcer("resources/model.conf", "resources/policy.csv")
checkError(err)
ok, err := e.Enforce(sub, obj, act)
checkError(err)
if ok {
fmt.Println("通过!")
} else {
fmt.Println("不通过!")
}
}
// 统一错误检查
func checkError(err error) {
if err != nil {
println(err.Error())
}
}
#怎么样,其实casbin 没有用这些例子的话其实大家会看得云里雾里,等把对应的用户和请求方法写出来就简单了。真实项目里无非就是通过中间件进行拦截,拿到对应的用户和可以访问的方法就OK了。下面还有基于多租户的RBAC方法我们可以修改演示一下;
四、RBAC 加上角色管理首先我们修改模型文件:
[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 = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
五、接下来我们修改一下我们的匹配文件:
p, admin, /posts, POST
p, xiaoluo, /posts/:id, PUT
p, test, /posts/:id, DELETE
g,test,admin
#说明,我们添加一个test这个用户属于admin角色,也就意味着我们上面的代码可以修改成:
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
)
func main() {
sub := "test"
obj := "/posts"
act := "POST"
e, err := casbin.NewEnforcer("resources/model.conf", "resources/policy.csv")
checkError(err)
ok, err := e.Enforce(sub, obj, act)
checkError(err)
if ok {
fmt.Println("通过!")
} else {
fmt.Println("不通过!")
}
}
// 统一错误检查
func checkError(err error) {
if err != nil {
println(err.Error())
}
}
#执行也是通过的,因为test这个用户是属于admin这个角色,后续的方法可以举一反三。我们的规则文件现在使用的是policy.csv文件显然不太好,后续我们应该是要完善到mysql数据库里做动态的权限管理并且和gin的中间件集成 。