从本篇文章开始,将由我来开始介绍访问控制框架的基础知识,本篇文章是 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 相关的一部分适配器:

img.png

可以看到支持的生态十分丰富。

简单使用

接下来就由我来简单演示下 casbin 的使用

model.conf

我们先创建 model.conf 文件,这个文件是访问模型,这里我采用了最简单的 ACL 访问控制模型,具体如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[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 文件来存储了特定的用户权限配置。具体如下:

1
2
p, alice, data1, read
p, bob, data2, read

这里可以简单解释为 alice 拥有对 data1 的读权限,bob 拥有对 data2 的读权限。

main.go

我们来简单测试下,在我们的 main.go 文件中输入相应代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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")
		// 拒绝请求,抛出异常
	}

}

编译执行,输出:

1
2022/01/12 17:22:52 alice can read data1

可以看到输出了正确的结果,我来修改下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 看看效果,具体代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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 表,具体表结构如下:

img_1.png

从这里可以看出 适配器的作用就是将 用户权限配置存储在不同的地方。

小结

到这里,casbin 的第一篇文章就介绍到这里,主要介绍了 casbin 的概念和适配器,简单的演示了使用方法。

本文的代码在这里可以看到: https://github.com/overstarry/casbin-demo

参考