时间:2021.12.06
环境:Windows
目的:Casbin简介与使用 希望对大家有帮助
说明:以官方文档为基础来讲解与拓展详解,并最终帮助大家在项目中使用!本文持续更新丰富内容 。。。
作者:Zhong QQ交流群:121160124 欢迎加入!
官方网站:Overview · Casbin
官方qq交流群:546057381
满招损 谦受益
失之毫厘 差之千里
业精于勤荒于嬉 行成于思毁于随
三人行必有我师焉
择其善者而从之 其不善者而改之
海纳百川 有容乃大
壁立千仞 无欲则刚
合抱之木 生于毫末
九层之台 起于累土
千里之行 始于足下
大家好!简单介绍下我自己,我是 谁并不重要,重要的是你想了解和掌握Casbin!进入正题!
我们假设你对Go语言有一定了解,不了解也无太多所谓,重点是Casbin的设计思想,Casbin努力成为与编程语言无关的,解耦的权限框架!致力于多种语言一致体验,直白来说,就是一套规则框架,在各种编程语言中稍微改动就可以与业务系统结合使用,使其成为与语言、语言框架无关且通用的权限管理系统。
本文内容比较多 请耐心阅读!!!绝对是最详细最干货的文章,没有之一!!!网上的关于Casbin的文章大多都读过,没发现很全面的。。。
Casbin 是什么?
我们先来看看官方对它的定义:
Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。
Casbin 可以:
Casbin 不能:
我们可以理解为Casbin做的是3A(认证、授权、审计)之中的授权这一步,也是主旨!
Casbin支持的编程语言
在不同语言中支持的特性
特性 |
Go |
Java |
Node.js |
PHP |
Python |
C# |
Delphi |
Rust |
C++ |
Lua |
Dart |
Exilir |
具体实施 |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
RBAC |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
ABAC |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
Scaling ABAC (eval()) |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
✅ |
✅ |
✅ |
Adapter |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
Management API |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
RBAC API |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
Batch API |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
✅ |
❌ |
❌ |
Filtered Adapter |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
✅ |
❌ |
❌ |
Watcher |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
Role Manager |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
✅ |
✅ |
❌ |
Multi-Threading |
✅ |
✅ |
✅ |
❌ |
✅ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
matcher中的‘in‘语法 |
✅ |
❌ |
❌ |
✅ |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
✅ |
✅ |
我们一直致力于让 Casbin 在不同的编程语言中拥有相同的特性。 但是现实总是不完美的。 上方的表格展示了当前的进度。 Watcher 和 Role Manager 的 ✅ 仅代表 Casbin 对该编程语言有接口, 是否实现了 watcher 或 role manager 接口则是另一回事了。
tips:特性支持持续更新中 ... ...
我们以Go为主、Python为辅通过demo来理解和使用Casbin,更多语言支持请访问官网
安装
golang
go get github.com/casbin/casbin/v2
python
pip install casbin
古人云 工欲善其事 必先利其器
官方推出的在线认证库工具 对于调试和理解很有用 建议多使用
方便体验定义Model、Policy、Request,验证并获取结果,得到你想要的的Model定义
在线工具:Casbin · An authorization library that supports access control models like ACL, RBAC, ABAC for Golang, Java, C/C++, Node.js, Javascript, PHP, Laravel, Python, .NET (C#), Delphi, Rust, Ruby, Swift (Objective-C), Lua (OpenResty), Dart (Flutter) and ElixirAn authorization library that supports access control models like ACL, RBAC, ABAC for Golang, Java, C/C++, Node.js, Javascript, PHP, Laravel, Python, .NET (C#), Delphi, Rust, Ruby, Swift (Objective-C), Lua (OpenResty), Dart (Flutter) and Elixirhttps://casbin.org/en/editor
先不着急深入理解它的各种概念,先来通过最基础的demo体验一下
我们创建一个文件夹随便什么名如demo,在demo里面创建三个文件main.go/model.conf/policy.csv,内容分别如下
main.go
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
"log"
)
func main() {
e, err := casbin.NewEnforcer("model.conf", "policy.csv")
if err != nil {
log.Fatalf("error: enforcer: %s", err)
}
sub := "alice" // 想要访问资源的用户。
obj := "data1" // 将被访问的资源。
act := "read" // 用户对资源执行的操作。
ok, err := e.Enforce(sub, obj, act)
if err != nil {
// 处理err
fmt.Println(err.Error())
}
if ok == true {
// 允许 do next ...
fmt.Printf("%s cat %s %s\n", sub, act, obj)
} else {
// 拒绝请求,抛出异常
fmt.Printf("%s cat't %s %s\n", sub, act, obj)
}
}
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
policy.csv
p, alice, data1, read
p, alice, data2, write
运行 go run main.go 输出下面的结果
alice cat read data1
如果将main.go中的obj := "data1"改为obj := "data2"那么将输出
alice cat't read data2
上面字面上的意思是alice可以read data1,不可以read data2,因为我们只给她write data2的权限!相信在这里肯定有人会感到迷惑,不明白什么意思,主要是因为和我们实际开发业务基于rustful的GET/POST/PUT/DELETE不一样!那么如下就明了
policy.csv
允许alice GET请求url /index查看主页
允许alice GET、POST url /user来获取用户信息和新增用户
p, alice, /index, GET
p, alice, /user, GET
p, alice, /user, POST
main.go
sub := "alice" // 想要访问资源的用户。
obj := "/index" // 将被访问的资源。
act := "GET" // 用户对资源执行的操作。
接着上面的说,alice我们视为一个请求主体这儿就是一个用户,data1是资源也就是访问操作的数据,read是一个动作,代表请求主体对资源的操作!简单来说就是谁可以对资源做什么,不可以对资源做什么!这种说法可以视为一种规则约束,那么将这种规则抽象出来就是上面的model.conf文件,也称为访问控制模型。规则有了,就要有实体配合才能发挥作用,policy.csv定义的就是具体的实例,就像盖一栋楼,首先要有设计人员设计施工图纸,有了图纸按照指引该建设什么,该避免什么就可以完成施工建造出具体的大楼!因此,在Casbin的架构里,核心就是model(模型)和policy(策略)!其它都是围绕它的具体实现和控制!理解了model与policy也就理解了Casbin,其中重点、难点就是model里面的概念与如何定义!
它是怎么工作的呢?
接下来的话对理解权限很重要
想想在平时的开发中尤其是前后端分离的开发模式,前端往往根据后端提供的API(应用程序接口)来访问服务器资源,这些API在前端来讲就是URI(统一资源定位符),也就如你在访问百度时浏览器地址栏输入的url:https://baidu.com,每一个url代表了服务器上的某个资源。例如,我们想要获取系统里所有的用户,输入127.0.0.1:8000/users,获取id为100的这个用户输入127.0.0.1:8000/users/100,后端程序根据url路由来调用对应的处理方法检查当前用户是否已登录,是否有权限访问这个url,如果有权限那么查询MySQL数据库等等一系列操作后最终将目标数据返回给前端展示,那么就完成了一次前后端的请求交互!url不同,对应的资源数据也就会不同!所以,从某种角度来说,对权限的控制其实就是对url的控制!一个用户有多少权限取决于她有多少个url的访问权限!
Casbin通过model定义规则约束,policy实例化model为一条条具体的规则来控制权限
下面是一个典型的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
在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件(model.conf)。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中结合RBAC角色和ABAC属性,并共享一组policy规则。
PERM模式由四个基础(请求、政策、效果、匹配)组成,描述了资源与用户之间的关系。
官网解释:
定义请求参数。基本请求是一个元组对象,至少需要主题(访问实体)、对象(访问资源) 和动作(访问方式)
例如,一个请求可能长这样: r={sub,obj,act}
它实际上定义了我们应该提供访问控制匹配功能的参数名称和顺序。
个人理解:
对应上面模型的request_definition部分 如下
[request_definition]
r = sub, obj, act
这条“规则”指明一次请求至少需要提供三个参数:请求主体、要访问的资源和请求方式,即前端访问API时提供当前请求的用户、要访问的url和动作(GET/POST...)
例如:alice /user/100/info GET alice用户的id为100 她要查询自己的个人信息
官网解释:
定义访问策略模式。事实上,它是在政策规则文件中定义字段的名称和顺序。
例如: p={sub, obj, act}
或 p={sub, obj, act, eft}
注:如果未定义eft (policy result),则策略文件中的结果字段将不会被读取, 和匹配的策略结果将默认被允许。
个人理解:
对应上面模型的policy_definition部分 如下
[policy_definition]
p = sub, obj, act
这条“规则”指明policy的格式应至少有三个参数:访问主体、访问对象和访问方式,与request_definition中的元素一一对应,不过这些实体一般是存在于数据库中的如MySQL默认生成的casbin数据库里的casbin_rule表,这就很好理解了,请求的时候(request)给了三个必要参数,拿着这三个参数来检索policys看是否有之对应的,也就是下面Matcher定义的规则
匹配请求和政策的规则。
例如: m = r.sub == p.sub && r.act == p.act && r.obj == p.obj
这个简单和常见的匹配规则意味着如果请求的参数(访问实体,访问资源和访问方式)匹配, 如果可以在策略中找到资源和方法,那么策略结果(p.eft
)便会返回。 策略的结果将保存在 p.eft
中。
它可以被理解为一种模型,在这种模型中,对匹配结果再次作出逻辑组合判断。
例如: e = some (where (p.eft == allow))
这句话意味着,如果匹配的策略结果有一些是允许的,那么最终结果为真。大白话来说就是请求一个url的结果无非就是允许和拒绝两种情况,如果查询到允许请求这个url,那么就通过(True),否则就拒绝(False)。
其实如果明白了model的话policy还是很容易理解的,无非是对应model规则的策略实体。
model和policy均有不同的存储方式
不同于policy,model只能用于加载而不能存储,为什么呢?只是因为model是一个文件吗?不是的,因为我们认为 model 不是动态组件,不应该在运行时进行修改,所以我们没有实现一个 API 来将 model 保存到存储中。想想一下,用于建筑高楼大厦的图纸在开工之后会改来改去吗?肯定不会的一般都是设计好之后开始建造的,除非有必要!
好消息是,官方提供了三种等效的方法来静态或动态地加载模型:
从 .CONF 文件中加载 model
当你向 Casbin 团队寻求帮助时,他们会给你这个 Casbin 最常用的方法,此方法对于初学者来说很容易理解并且便于分享。
从代码加载 model
模型可以从代码中动态初始化,不需要使用 .CONF
从字符串加载的 model
或者也可以从多行字符串加载整个模型文本。这种方法的优点是您不需要维护模型文件。
具体方法参照官网实例,不再赘述!
上面说到model不会经常变化所以不能存储,那么policy呢?答案是肯定需要存储!
在Casbin中,策略存储作为适配器实现。
在Casbin中,policy(策略)存储作为adapter(Casbin的中间件) 实现。 policy理论上会经常变化,Casbin用户可以使用adapter从存储中加载策略规则 (aka LoadPolicy()
) 或者将策略规则保存到其中 (aka SavePolicy()
)。 demo中用于演示我们从一个csv文件读取policy,但在实际项目中,我们更多使用数据库存储,如MySQL、MongoDB等,使用数据库更方便管理,对于巨量策略条目性能也更高!Casbin官方提供了完整的适配器列表供选择,包含了各种主流编程语言以及各种主流数据库、云及文件如csv等的支持,有官方的也有第三方的具体请参照官网!
我们以Go语言、MySQL数据库和gorm orm库为例
安装gorm-adapter
go get github.com/casbin/gorm-adapter/v3
代码如下
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
gormadapter "github.com/casbin/gorm-adapter/v3"
_ "github.com/go-sql-driver/mysql"
)
func main() {
a, _ := gormadapter.NewAdapter("mysql", "root:123456@tcp(127.0.0.1:3306)/")
e, _ := casbin.NewEnforcer("model.conf", a)
ok, err := e.Enforce("alice", "data1", "read")
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(ok)
}
gormadapter.NewAdapter("mysql", "root:123456@tcp(127.0.0.1:3306)/your_database") 如果/your_database没有指定数据库那么将默认创建名为casbin的数据库,并且创建一张表名为casbin_rule,字段如下
插入一条数据
运行go run main.go 将输出 true 意味着用户alice拥有read资源data1的权限,其它权限均无!
以上实例我们创建了gorm类型adapter,它使用MySQL作为policy存储容器,并使用默认生成的数据库casbin中的casbin_rule表存储具体的policy,返回*Adapter与err对象,然后调用NewEnforcer方法加载模型文件model.conf与适配器a,对alice鉴权。在此,我们需要知道并记住的是,Casbin默认将policy(策略)加载到内存中,如果改变了存储容器内的策略数据需要执行LoadPolicy()方法重载策略到内存才能生效!方便的是,我们在官方提供的API中不需要手动去调用,这些方法底层已实现,除非特别说明!由此指出,如果我们自己实现某些会改变策略方法的话需要实现这个方法,否则就要手动调用或重启,常见的问题有程序在运行时,手动往数据表插入一条策略去测试并未生效!查询等不涉及改变数据表数据的操作不需要有此操作!
疑问与思考
古德,你已经迫不及待的把Casbin部署在公司项目中,为了谨慎起见,你只是在两台服务器部署了你的项目(也算是最小的集群了),并给你的同事如花小姐姐建立了账户分配了角色,邀请她体验和测试。but,what?正当你满心欢喜的期待时,阿花却告诉你:sorry!你一脸茫然,于是自己也测试了一下,发现了问题:请求一次正常,下一次请求401,再下一次又是正常访问,如此循环反复,这是为什么呢?于是你陷入了思考......
既然策略加载在内存 如何在分布式集群部署项目中保持策略的同步呢?答案是Watcher
何为观察者?
我们造 古时候通讯没有现在发达方便 但前人有智慧啊!他们设置了望楼并发明了各种各样的旗语,用来主动发现目光所及范围内的情况或者和其它望台、其他人进行“交流”,来达到第一时间“共享”消息或者发布指令的目的。
为什么需要观察者?
通过前面的介绍我们知道,策略默认是从文件或数据库读取之后加载到内存中的,只有在创建、更新、删除等(没有等?)操作会改变策略的时候触发LoadPolicy()来重载策略到当前服务器的内存,当我们在多台服务器以负载均衡的方式部署项目时,在一次请求中“改变”了策略,那么处理这条请求的服务器会重载策略到当前服务器的内存,但是其它服务器不知道,因为没有“人”通知它,所以它们自顾自乐呵乐呵的期待着下一个用户的请求。比如,你和一群小伙伴相约去公园踏春赏景,精心装扮一番后去见你的女神,结果发现她没来,只有小红说给她发了信息说不来了,而小红以为大家都收到消息了没有再通知其他人,那么如果女神群发消息的话大家就可以都知道了!所以,上面的需求就需要一个机制来完成策略同步(当策略有变化时,所有服务端应同步最新策略保持一致),于是乎,Watcher应运而生!
tips:单机不需要的哦!
推荐几个不错的集成了Casbin鉴权的开源项目 出场顺序不排名
github:https://github.com/flipped-aurora/gin-vue-admin
项目简介
基于vite+vue3+gin搭建的开发基础平台,集成jwt鉴权,权限管理,动态路由,分页封装,多点登录拦截,资源权限,上传下载,代码生成器,表单生成器等开发必备功能,五分钟一套CURD前后端代码。
github:https://github.com/go-admin-team/go-admin
项目简介
github:https://github.com/LyricTian/gin-admin
项目简介
github:https://github.com/GoAdminGroup/go-admin/
项目简介
A golang framework helps gopher to build a data visualization and admin panel in ten minutes
GoAdmin是一个基于 golang 面向生产的数据可视化管理平台搭建框架,可以让你使用简短的代码在极短时间内搭建起一个管理后台。
github:https://github.com/lanyulei/ferry
项目简介
本系统是集工单统计、任务钩子、权限管理、灵活配置流程与模版等等于一身的开源工单系统,当然也可以称之为工作流引擎。 致力于减少跨部门之间的沟通,自动任务的执行,提升工作效率与工作质量,减少不必要的工作量与人为出错率。
tips:以上项目是笔者发现的几个不错的开源项目,项目简介引用项目本身介绍,对于入门和理解使用Casbin在项目中的应用有一定的借鉴作用。当然还有很多优秀的项目等待笔者和你的发现!
QQ交流群:121160124 欢迎加入!
微信公众号 写了很久, 对你有帮助的话,可以请喝咖啡(便宜的奶茶吧!)