从本篇文章开始,将由我来开始介绍访问控制框架的基础知识,本篇文章是 casbin 系列文章的第一篇,主要介绍一些 casbin 的概念和基础知识。
概述
casbin 是一个开源的访问控制框架,它的目标是让开发人员可以更加简单的控制访问控制,支持多种访问控制模型,支持多种编程语言(各种编程语言支持的程度可以查看官网的文档)。
casbin 可以
- 支持自定义请求的格式,默认的请求格式为{subject, object, action}。
- 具有访问控制模型model和策略policy两个核心概念。
- 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。
- 支持内置的超级用户 例如:root 或 administrator。超级用户可以执行任何操作而无需显式的权限声明。
- 支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo*
Casbin 不支持的是:
身份认证 authentication(即验证用户的用户名和密码),Casbin 只负责访问控制。应该有其他专门的组件负责身份认证,然后由 Casbin 进行访问控制,二者是相互配合的关系。
管理用户列表或角色列表。 Casbin 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系。
工作原理
在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中结合RBAC角色和ABAC属性,并共享一组policy规则。
adapters
在Casbin中,策略存储作为adapter(Casbin的中间件) 实现。 Casbin用户可以使用adapter从存储中加载策略规则 (aka LoadPolicy()) 或者将策略规则保存到其中 (aka SavePolicy())。 为了保持代码轻量级,我们没有把adapter代码放在主库中。
casbin 目前支持的官方的和第三方适配器有许多,这里只截取 go 相关的一部分适配器:
可以看到支持的生态十分丰富。
简单使用
接下来就由我来简单演示下 casbin 的使用
model.conf
我们先创建 model.conf 文件,这个文件是访问模型,这里我采用了最简单的 ACL 访问控制模型,具体如下
[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
policy.csv
我们采用 File Adapter 适配器来讲解,我们创建 policy.csv 文件来存储了特定的用户权限配置。具体如下:
p, alice, data1, read
p, bob, data2, read
这里可以简单解释为 alice 拥有对 data1 的读权限,bob 拥有对 data2 的读权限。
main.go
我们来简单测试下,在我们的 main.go 文件中输入相应代码:
package main
import (
"github.com/casbin/casbin/v2"
"log"
)
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
panic(err)
}
sub := "alice" // 想要访问资源的用户。
obj := "data1" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
ok, err := e.Enforce(sub, obj, act)
if err != nil {
panic(err)
}
if ok == true {
log.Println("alice can read data1")
// 允许alice读取data1
} else {
log.Println("alice can not read data1")
// 拒绝请求,抛出异常
}
}
编译执行,输出:
2022/01/12 17:22:52 alice can read data1
可以看到输出了正确的结果,我来修改下代码:
package main
import (
"github.com/casbin/casbin/v2"
"log"
)
func main() {
e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
if err != nil {
panic(err)
}
sub := "alice" // 想要访问资源的用户。
obj := "data2" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
ok, err := e.Enforce(sub, obj, act)
if err != nil {
panic(err)
}
if ok == true {
log.Println("alice can read data2")
// 允许alice读取data1
} else {
log.Println("alice can not read data2")
// 拒绝请求,抛出异常
}
}
编译运行输出: 2022/01/12 17:24:08 alice can not read data2
输出了 alice 不能读取 data2 资源
Ent adapter
我们将 File Adapter 适配器改为 Ent adapter 看看效果,具体代码如下:
package main
import (
"github.com/casbin/casbin/v2"
entadapter "github.com/casbin/ent-adapter"
"log"
)
func main() {
a, err := entadapter.NewAdapter("postgres", "host=127.0.0.1 user=postgres password=123456 dbname=casbin port=5432 sslmode=disable TimeZone=UTC")
if err != nil {
panic(err)
}
e, err := casbin.NewEnforcer("./model.conf", a)
if err != nil {
panic(err)
}
sub := "alice" // 想要访问资源的用户。
obj := "data2" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
ok, err := e.Enforce(sub, obj, act)
if err != nil {
panic(err)
}
if ok == true {
log.Println("alice can read data2")
// 允许alice读取data1
} else {
log.Println("alice can not read data2")
// 拒绝请求,抛出异常
}
}
编译执行,查看数据库,可以看到产生了一个 casbin_rules 表,具体表结构如下:
从这里可以看出 适配器的作用就是将 用户权限配置存储在不同的地方。
小结
到这里,casbin 的第一篇文章就介绍到这里,主要介绍了 casbin 的概念和适配器,简单的演示了使用方法。
本文的代码在这里可以看到: https://github.com/overstarry/casbin-demo