解锁灵活与高效——Casbin
今天这篇博客的标题看起来就很有逼格,不错,就是我们万能的 ChatGPT起的。当然,还是稍微改了一下,它给的实在是太有逼格了,我不太敢用。
每隔一段时间使用ChatGPT就会给我一种AI已经发展得这么牛逼的感觉,但是每次用它来解决一些实际问题的时候又会让我觉得它是个智障。
不得不说,ChatGPT在很多方面都已经发展到了可以取代一大部分人的水平了。从它出现之后,我就没有自己动手写过思政方面的任何东西,全是它完成的,属实是有点东西。写点题外话让破防的自己慢慢恢复一下,题外话到此为止。
本文要记录的是一个访问控制库——Casbin。
简介
Casbin 是一个开源的访问控制框架,用于实现权限管理和访问控制的功能。它提供了一种简单而灵活的访问控制模型,可以用于保护应用程序、服务或其他系统中的资源。Casbin 的设计目标是提供一种通用的访问控制模型,并支持多种编程语言。
Casbin 的核心概念是访问控制模型和策略。访问控制模型定义了一组规则,用于描述谁可以访问哪些资源以及在什么条件下可以访问。策略是基于模型定义的规则,描述了实际的访问控制规则集合。
Casbin 支持多种访问控制模型,包括基于角色的访问控制(RBAC)、访问控制列表(ACL)和属性访问控制(ABAC)等。它还提供了灵活的策略管理机制,可以将策略存储在不同的持久化存储中,如文件、数据库或远程服务。
使用 Casbin,你可以轻松地实现细粒度的权限管理,定义和管理角色、资源和操作之间的访问规则,并在应用程序中进行访问控制的验证。无论是 Web 应用程序、API 服务还是其他类型的系统,Casbin 都可以帮助你实现安全的访问控制机制。
关键性概念
- 模型(Model):模型定义了访问控制策略的基本结构和规则。它由多个规则组成,每个规则包含多个字段,如”sub”(Subject,主体,表示用户)、”obj”(Object,对象,表示资源)、”act”(Action,动作,表示操作)等。
- 策略(Policy):策略是实际的访问控制规则集合,用于定义谁可以对什么资源执行什么操作。Casbin 支持多种类型的策略,包括基于角色的访问控制(Role-Based Access Control,RBAC)、基于对象的访问控制(Object-Based Access Control,OBAC)等。
- 主体(Subject):主体代表用户或者实体,它通常通过唯一标识符(如用户名、角色等)进行识别。主体在访问控制中被授予特定的权限。
- 对象(Object):对象代表系统中的资源,可以是文件、数据库表、API 端点等。对象具有特定的属性和标识符,用于确定访问控制规则。
- 动作(Action):动作表示主体对对象执行的操作或行为,如读取、写入、删除等。动作定义了主体对资源的权限。
- 策略存储(Policy Store):策略存储用于存储和管理策略规则。Casbin 支持多种策略存储后端,如内存、文件、数据库等。
通过定义模型、策略和访问请求,Casbin 可以进行访问控制决策,判断是否允许主体执行特定的操作。它提供了灵活的访问控制模型和丰富的策略管理功能,使开发人员能够轻松实现细粒度的权限管理和访问控制策略。
和JWT
有什么关系
提到鉴权,笔者第一个学习到的是 JWT。之前面试官会问,在你的项目里面是怎么实现鉴权操作的。我会统一回答,使用JWT生成token,然后解析出用户数据,最后在数据库中查找相关信息。其实我也不知道这样的回答是不是正确的,反正那么多场面试到最后都是不了了之,这都不是重点,重点是不要把 casbin 和 JWT 搞混了。
JWT(JSON Web Token)鉴权和 Casbin 鉴权在权限管理体系中扮演着不同但互补的角色。以下是它们的关系和区别:
作用和功能
JWT 鉴权:
- 作用: JWT 主要用于认证(Authentication),即验证用户的身份。它确保请求的发起者是合法用户。
- 功能: JWT 是一种自包含的令牌,通常包含用户ID、用户名、角色等信息,并且通过数字签名进行验证,确保数据的完整性和真实性。
Casbin 鉴权:
- 作用: Casbin 主要用于授权(Authorization),即确定已认证用户是否有权限执行某个操作。它基于访问控制模型和策略来决定权限。
- 功能: Casbin 通过模型和策略定义权限规则,可以实现复杂的权限管理,如基于角色的访问控制(RBAC)和基于属性的访问控制(ABAC)。
工作流程
JWT 鉴权:
- 用户登录并提供凭据(用户名和密码)。
- 服务器验证凭据,如果验证通过,生成 JWT 并返回给用户。
- 用户在后续请求中附带 JWT(通常在 HTTP 头部的 Authorization 字段)。
- 服务器验证 JWT 的签名和有效期,确定用户身份。
Casbin 鉴权:
- 用户的请求经过 JWT 验证后,服务器获取用户身份信息。
- 服务器根据用户身份信息(如用户ID或角色),请求资源和操作。
- 使用 Casbin 的 Enforcer 进行权限校验,判断用户是否有权执行该操作。
- 根据校验结果,允许或拒绝用户的请求。
关系和结合
- 关系: JWT 和 Casbin 是互补的。JWT 负责认证,确保请求者的身份;Casbin 负责授权,确保请求者有权限执行特定操作。
- 结合: 在实际应用中,通常会先进行 JWT 验证,确认用户身份后,再使用 Casbin 进行权限检查。
区别
- JWT 验证身份,Casbin 验证权限: JWT 确认用户是谁,Casbin 确认用户能做什么。
- JWT 是一种令牌,Casbin 是一种访问控制工具: JWT 是一种令牌机制,用于在无状态的环境中传递用户信息。Casbin 是一个权限管理库,用于根据预定义的策略进行权限判断。
- 使用时机不同: JWT 通常在每次请求的开始进行验证,以确定用户身份。而 Casbin 的权限校验则在身份验证通过后,根据请求的具体资源和操作进行。
结合实例
在一个 Web 应用中,结合 JWT 和 Casbin 的典型流程如下:
用户登录:
- 用户提交登录信息。
- 服务器验证登录信息,生成 JWT 并返回给用户。
用户请求资源:
- 用户在请求头中附带 JWT 访问资源。
- 服务器验证 JWT,确定用户身份。
权限校验:
- 服务器提取用户身份、请求资源和操作。
- 使用 Casbin Enforcer 进行权限检查,判断用户是否有权访问该资源或执行该操作。
处理请求:
- 如果权限校验通过,处理请求并返回结果。
- 如果权限校验不通过,返回 403 Forbidden 响应。
通过这种方式,JWT 和 Casbin 可以无缝结合,实现安全且灵活的认证和授权机制。
能做什么?又不能做什么?
我们从上面的回答就能看出,这两个东西其实是结合在一起使用的,在本项目中也是如此。在更加详细地了解其原理之前,我们先来看一下在什么情况下会使用到它。
Can Do
- 以典型的 {主体、对象、动作} 形式或您定义的自定义形式形成策略。支持允许和拒绝授权。
- 具有访问控制模型
model
和策略policy
两个核心概念。 - 支持 RBAC 中的多层角色继承,不止主体可以有角色,资源也可以具有角色。
- 支持内置超级用户,如
root
或administrator
。 超级用户可以在没有明确权限的情况下做任何事情。 - 提供多个内置操作符,支持规则匹配。例如,
keyMatch
可将资源键/foo/bar
映射到模式/foo*
。
Can‘t do
- 验证(又称用户登录时验证用户名和密码):Casbin 仅负责权限管理,不负责用户身份验证。身份验证需要通过其他机制(如 JWT、OAuth 等)实现。
- 管理用户列表或角色列表:Casbin 不提供用户管理功能,如用户注册、密码管理等。这些功能通常由用户管理系统(如 LDAP、Active Directory)或自定义用户管理模块实现。
- 数据加密和解密:Casbin 不处理数据的加密和解密任务。这需要通过其他安全工具和库来实现。
- 网络安全:Casbin 不直接处理网络安全问题,如防火墙、防御 DDoS 攻击等。这需要通过网络安全设备和配置来实现。
- 审计和日志管理:虽然 Casbin 可以配合其他工具进行权限使用的审计和日志记录,但它自身不具备全面的审计和日志管理功能。
项目管理用户、角色或密码列表更加方便。用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储 RBAC 方案中用户和角色之间的映射关系。
快速使用
讲了那么对概念,想必对casbin也有了一个大概的了解了。拿就来看一下要怎么使用吧:
1 | package main |
这里面涉及到两个配置文件,分别是 model.conf 和 policy.csv 文件。
内容如下:
model.conf
1 | [request_definition] |
policy.csv
1 | p, member, /posts, GET |
这两个配置文件你现在就暂时先理解为 model.conf 定义了匹配规则,policy.csv 定义了权限组等权限。
直接运行起来,你会看到控制台输出:
1 | 通过! |
假如此时我们把 act 改成 POST 就不能通过了。
学习过大概怎么使用后,就要更加深入地学习了。
模型
casbin 支持多种模型,适用于多种业务场景
- ACL (Access Control List, 访问控制列表)
- 具有 超级用户 的 ACL
- ACL without users: 这对于没有身份验证或用户登录的系统特别有用。
- ACL without resources: 在某些场景中,目标是一种类型的资源,而不是单个资源。可以使用 “写文章” 和 “读日志” 等权限。这不会控制对特定文章或日志的访问。
- RBAC (基于角色的访问控制)
- RBAC with resource roles: 用户和资源可以同时拥有角色(或组)。
- RBAC with domains/tenants: 用户可以为不同的域 / 租户拥有不同的角色集。
- ABAC (Attribute-Based Access Control): 像 “resource.Owner” 这样的语法糖可以用来获取资源的属性。
- RESTful: 支持 “/res/*”、“/res/:id” 等路径,以及 “GET”、“POST”、“PUT”、“DELETE” 等 HTTP 方法。
- Deny-override: 在允许拒绝覆盖的情况下,允许和拒绝授权都受支持。
- Priority: 策略规则可以按优先级排列,类似于防火墙规则。
一般会通过使用场景确定模型,然后在官网实例中查询模型,直接使用。
存储
casbin 支持通过加载存储的模型、策略、策略子集实现动态调整。
模型的存储
与策略不同,模型只能加载,不能保存。我们认为,模型不是动态组件,不应在运行时修改,因此我们没有实现将模型保存到存储中的应用程序接口。
不过,有一个好消息。我们提供了三种等效的方法来静态或动态加载模型:
- 从
.conf
文件中加载 model - 从代码加载 model
- 从字符串加载 model
策略地存储
在 casbin 中,策略存储作为 适配器 来实现
- 从
.csv
文件加载 - 从后端存储加载实现
策略子集加载
一些 adapter 支持过滤策略管理。 这意味着 Casbin 加载的策略是根据给定过滤器存储在数据库中的策略的子集。这样就能在大型多租户环境中高效执行策略,因为在这种环境中,解析整个策略会成为性能瓶颈。
为了防止意外数据丢失,当策略已经加载, SavePolicy 方法会被禁用。
要使用支持的 adapter 处理过滤后的策略,只需调用 LoadFilteredPolicy
方法。 过滤器参数的有效格式取决于所用的适配器。 为了防止意外数据丢失,当策略已经加载, SavePolicy
方法会被禁用。
执行器
Casbin 使用配置文件来定义访问控制模型。
例如使用文件记录模型和策略存储文件,作为两个配置文件:model.conf
和 policy.csv
。model.conf
保存访问模型,而 policy.csv
保存具体的用户权限配置。Casbin
的使用非常简单。我们只需创建一个主要结构:enforcer
。当构造这个结构的时候,model.conf
和 policy.csv
将会被加载。
换句话说,要创建一个 Casbin 执行器,您需要提供一个模型和一个适配器。
1 | e, err := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv") |
在 Casbin 中,执行器(Enforcer)是负责执行访问控制策略的核心组件。它提供了一系列方法,用于判断主体是否被授予对特定资源执行特定操作的权限。
执行器的主要功能包括:
- 加载策略:执行器可以从不同的策略存储(如文件、数据库)中加载访问控制策略。它可以读取和解析策略规则,并将其转换为内部模型进行处理。
- 决策判断:执行器可以根据加载的策略和传入的访问请求,进行访问控制决策。它会根据模型中定义的规则和策略,判断主体是否有权限执行特定的操作。
- 策略管理:执行器提供了管理策略的方法,可以动态地添加、修改或删除策略规则。这使得开发人员可以根据需要对访问控制策略进行灵活的调整和管理。
- 角色管理:执行器支持角色(Role)的管理,包括角色的添加、删除和关联。角色可以用于实现基于角色的访问控制(RBAC)模型。
通过执行器,开发人员可以轻松地集成 Casbin 的访问控制功能到他们的应用程序中。执行器提供了简单而强大的 API,使开发人员能够方便地进行访问控制决策和策略管理。
几种执行器
- 通用执行器:最基础的执行器,Enforcer 是用户与 Casbin 策略和模型交互的基本结构。
- 带缓存的执行器:
CachedEnforcer
基于Enforcer
,支持使用map
将请求的判断结果(是否具有能力)缓存在内存中。它能在指定的过期时间内清除缓存。此外,它还通过读写锁保证线程安全。您可以使用EnableCache
启用缓存评估结果(默认为启用)。CachedEnforcer
的其他 API 方法与Enforcer
相同。 - 分布式执行器:
DistributedEnforcer
支持分布式集群中的多个实例。它为调度器封装了SyncedEnforcer
。官方的分布式执行器无法指定适配器,使用内置的适配器,而且通过 HTTP 接口实现一致性。 - 同步执行器:
SyncedEnforcer
同步执行器提供并发功能,是线程安全的。 - 带缓存的同步执行器:
SyncedCachedEnforcer
,同步执行器和带缓存的执行器结合
如何挑选:
- 如果是简单环境,使用通用执行器:
casbin.NewEnforcer()
- 如果需要提高判断结果的并发,则使用带缓存的执行器:
casbin.NewCachedEnforcer()
- 如果是分布式的场景,例如多个服务器集群中,每个集群都需要有 casbin 实例,而且需要有独立的后端存储,则使用分布式执行器:
casbin.NewDistributedEnforcer()
- 如果会频繁变动策略,而且是并发场景,则使用同步执行器:
casbin.NewSyncedEnforcer()
- 如果既要频繁变动策略,也要求判断结果的高并发,则使用带缓存的同步执行器:
casbin.NewSyncedCachedEnforcer()
在当下云原生的场景下,部署在 k8s
上,并且后端使用同一个存储,多个实例,推荐使用同步执行器或者带缓存的同步执行器。
适配器
在 Casbin 中,策略存储作为 adapter (Casbin 的中间件) 实现。 Casbin 用户可以使用 adapter 从存储中加载策略规则 (LoadPolicy()
) 或者将策略规则保存到其中 (SavePolicy()
)。
- 如果使用显式或隐式 adapter 调用
casbin.NewEnforcer()
,策略将自动加载。- 可以调用
e.LoadPolicy()
来从存储中重新加载策略规则。- 如果 adapter 不支持
Auto-Save
特性,则在添加或删除策略时不能将策略规则自动保存回存储器。 你必须手动调用SavePolicy()
来保存所有的策略规则
当然,以下是使用 Go 语言结合 Casbin 和多种适配器的代码示例:
使用文件适配器
文件适配器是 Casbin 的默认适配器,使用本地文件系统存储策略。
1 | package main |
使用 MySQL 适配器
使用 MySQL 数据库存储策略,需要安装 github.com/casbin/mysql-adapter/v2
适配器。
1 | package main |
使用 PostgreSQL 适配器
使用 PostgreSQL 数据库存储策略,需要安装 github.com/casbin/pg-adapter
适配器。
1 | package main |
使用 MongoDB 适配器
使用 MongoDB 数据库存储策略,需要安装 github.com/casbin/mongodb-adapter/v3
适配器。
1 | package main |
使用 Redis 适配器
使用 Redis 数据库存储策略,需要安装 github.com/casbin/redis-adapter/v2
适配器。
1 | package main |
自定义适配器
如果现有适配器不能满足需求,可以实现自定义适配器。自定义适配器需要实现 casbin.Adapter
接口的以下方法:
LoadPolicy(model model.Model) error
SavePolicy(model model.Model) error
AddPolicy(sec string, ptype string, rule []string) error
RemovePolicy(sec string, ptype string, rule []string) error
RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error
这是一个自定义适配器的示例:
1 | package main |
通过这些示例,可以看到如何使用 Casbin 和不同的适配器实现权限管理,具体选择哪种适配器取决于应用的具体需求和环境。
监视器
casbin 支持使用分布式消息系统(如 etcd)来保持多个 Casbin 执行器实例之间的一致性。这样,用户就可以同时使用多个 Casbin 执行器来处理大量权限检查请求。
与策略存储适配器类似,casbin 在主库中不包含监视器代码。任何对新消息系统的支持都应该作为监视程序来实现。
关于监视器的完整列表建议去casbin官网查看。
推荐使用 Redis ,内部是通过 redis 的 channel 实现多个实例之间通讯。
调度器
调度程序提供了一种同步策略增量变化的方法。它们应基于 Raft 等一致性算法,以确保所有执行者实例的一致性。通过调度器,用户可以轻松建立分布式集群。
调度器的方法分为两部分。 第一部分是与 Casbin 结合的方法。这些方法应在 Casbin 内部调用。用户可以使用 Casbin 本身提供的更完整的 API。
另一部分是调度器自身定义的方法,包括调度器初始化方法和不同算法提供的不同功能,如动态成员资格和配置更改。
我们希望调度器只在运行时确保 Casbin 执行器的一致性。因此,如果策略在初始化时不一致,调度程序将无法正常工作。用户需要在使用调度程序前确保所有实例的状态一致。
调度器的使用场景更合适分布式的环境下。
角色管理器
角色管理器用于管理 Casbin 中的 RBAC 角色层次结构(用户角色映射)。 角色管理器可从 Casbin 策略规则或外部来源(如 LDAP、Okta、Auth0、Azure AD 等)检索角色数据。我们支持不同的角色管理器实现。 为了保持轻量级,我们在主库中不包含角色管理器代码(默认角色管理器除外)。
总结
上面一共介绍了casbin的五个组件,那么它们之间有什么关系呢?
在 Casbin 中,执行器(Enforcer)、适配器(Adapter)、监视器(Watcher)、调度器(Dispatcher)和角色管理器(RoleManager)是五个核心组件,它们各自负责不同的功能,但在 Casbin 的权限管理系统中有着密切的联系。
- 适配器与执行器: 适配器为执行器提供加载策略的数据源,执行器在权限判断时依赖适配器加载的策略数据。
- 监视器与适配器: 监视器与适配器协作,适配器加载策略数据后,监视器可以持续监控数据的变化。
- 调度器与执行器: 调度器负责管理执行器的并发访问,确保执行器在多线程环境下的安全运行。
- 角色管理器与执行器: 角色管理器与执行器配合,执行器在权限判断时可能涉及角色信息,需要通过角色管理器查询和验证角色。
总的来说,执行器是 Casbin 权限判断的核心,适配器用于加载和保存策略数据,监视器用于监控数据的变化,调度器用于管理并发访问,角色管理器用于管理角色信息。这些组件之间紧密联系并协作,共同构建了 Casbin 的权限管理系统。
总结
嘿,这才是真正的总结,我的博客文章怎么可能有一个那么正经的总结呢。
上面已经基本把casbin中会使用到的东西讲的很清楚了,在本项目中也是结合着 JWT 来实现的权限控制。基本流程为,先使用 JWT 检查是否正常登录,若正常登录再检查该用户的操作权限,两者相辅相成。
ok,那么又增加了一个在面试时能讲的内容,但是孩子现在根本就没有面试的机会,该说自己是运气不好呢,还是脑子里根本就没有那么多东西,所以才会一次又一次地被牵着鼻子走。再沉淀沉淀吧,先不急着找实习(其实是找不到)。
现在觉得要学的东西好像还有很多,一直赶着找实习会让我根本就没法集中去学习,老是速成也会一直忘,还是放慢脚步吧。
其实在学完 casbin 鉴权之后,这个开源项目里边比较新的东西就差不多结束了,还能继续学的就是一些数据库的操作了,不过我觉得这个大佬好像不是很想使用其他数据库,也基本上都没有什么能学的。不过 MongoDB 还是要学一下的。