casbin

简介

Casbin是一个开源的访问控制框架,用于实现权限管理和访问控制的功能。它提供了一种简单而灵活的访问控制模型,可以用于保护应用程序、服务或其他系统中的资源。Casbin的设计目标是提供一种通用的访问控制模型,并支持多种编程语言。

Casbin的核心概念是访问控制模型和策略。访问控制模型定义了一组规则,用于描述谁可以访问哪些资源以及在什么条件下可以访问。策略是基于模型定义的规则,描述了实际的访问控制规则集合。

Casbin支持多种访问控制模型,包括基于角色的访问控制(RBAC)、访问控制列表(ACL)和属性访问控制(ABAC)等。它还提供了灵活的策略管理机制,可以将策略存储在不同的持久化存储中,如文件、数据库或远程服务。

使用Casbin,你可以轻松地实现细粒度的权限管理,定义和管理角色、资源和操作之间的访问规则,并在应用程序中进行访问控制的验证。无论是Web应用程序、API服务还是其他类型的系统,Casbin都可以帮助你实现安全的访问控制机制。

https://casbin.org/zh/docs/overview

几个关键概念

Casbin是一个开源的访问控制库,用于实现权限管理和访问控制策略。它基于访问控制模型(Access Control Model)和策略(Policy)来管理用户对资源的访问权限。

在Casbin中,有几个关键性的概念:

  1. 模型(Model):模型定义了访问控制策略的基本结构和规则。它由多个规则组成,每个规则包含多个字段,如”sub”(Subject,主体,表示用户)、”obj”(Object,对象,表示资源)、”act”(Action,动作,表示操作)等。

  2. 策略(Policy):策略是实际的访问控制规则集合,用于定义谁可以对什么资源执行什么操作。Casbin支持多种类型的策略,包括基于角色的访问控制(Role-Based Access Control,RBAC)、基于对象的访问控制(Object-Based Access Control,OBAC)等。

  3. 主体(Subject):主体代表用户或者实体,它通常通过唯一标识符(如用户名、角色等)进行识别。主体在访问控制中被授予特定的权限。

  4. 对象(Object):对象代表系统中的资源,可以是文件、数据库表、API端点等。对象具有特定的属性和标识符,用于确定访问控制规则。

  5. 动作(Action):动作表示主体对对象执行的操作或行为,如读取、写入、删除等。动作定义了主体对资源的权限。

  6. 策略存储(Policy Store):策略存储用于存储和管理策略规则。Casbin支持多种策略存储后端,如内存、文件、数据库等。

通过定义模型、策略和访问请求,Casbin可以进行访问控制决策,判断是否允许主体执行特定的操作。它提供了灵活的访问控制模型和丰富的策略管理功能,使开发人员能够轻松实现细粒度的权限管理和访问控制策略。

能做什么

  1. 以典型的 { 主体、对象、动作 } 形式或您定义的自定义形式形成策略。支持允许和拒绝授权。
  2. 具有访问控制模型 model 和策略 policy 两个核心概念。
  3. 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。
  4. 支持内置超级用户,如 rootadministrator。 超级用户可以在没有明确权限的情况下做任何事情。
  5. 提供多个内置操作符,支持规则匹配。例如,keyMatch 可将资源键 /foo/bar 映射到模式 /foo*

不能做什么

  1. 验证(又称用户登录时验证用户名和密码)
  2. 管理用户列表或角色列表。

项目管理用户、角色或密码列表更加方便。用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系。

开始使用

1
go get github.com/casbin/casbin/v2

模型

casbin支持多种模型,适用于多种业务场景

  1. ACL (Access Control List, 访问控制列表)
  2. 具有 超级用户 的 ACL
  3. ACL without users: 这对于没有身份验证或用户登录的系统特别有用。
  4. ACL without resources: 在某些场景中,目标是一种类型的资源,而不是单个资源。可以使用“写文章”和“读日志”等权限。这不会控制对特定文章或日志的访问。
  5. RBAC (基于角色的访问控制)
  6. RBAC with resource roles: 用户和资源可以同时拥有角色(或组)。
  7. RBAC with domains/tenants: 用户可以为不同的域/租户拥有不同的角色集。
  8. ABAC (Attribute-Based Access Control): 像“resource.Owner”这样的语法糖可以用来获取资源的属性。
  9. RESTful: 支持“/res/*”、“/res/:id”等路径,以及“GET”、“POST”、“PUT”、“DELETE”等HTTP方法。
  10. Deny-override: 在允许拒绝覆盖的情况下,允许和拒绝授权都受支持。
  11. Priority: 策略规则可以按优先级排列,类似于防火墙规则。

https://casbin.org/zh/docs/supported-models

一般会通过使用场景确定模型,然后在官网实例中查询模型,直接使用。

存储

casbin 支持通过加载存储的模型、策略、策略子集实现动态调整。

模型的存储

与策略不同,模型只能加载,不能保存。我们认为,模型不是动态组件,不应在运行时修改,因此我们没有实现将模型保存到存储中的应用程序接口。

不过,有一个好消息。我们提供了三种等效的方法来静态或动态加载模型:

  • .conf 文件中加载 model
  • 从代码加载 model
  • 从字符串加载 model

策略的存储

在 casbin 中,策略存储作为 适配器 来实现

  • .csv 文件加载
  • 从后端存储加载实现

策略子集加载

一些adapter支持过滤策略管理。 这意味着 Casbin 加载的策略是根据给定过滤器存储在数据库中的策略的子集。这样就能在大型多租户环境中高效执行策略,因为在这种环境中,解析整个策略会成为性能瓶颈。

为了防止意外数据丢失,当策略已经加载, SavePolicy 方法会被禁用。

要使用支持的adapter处理过滤后的策略,只需调用 LoadFilteredPolicy 方法。 过滤器参数的有效格式取决于所用的适配器。 为了防止意外数据丢失,当策略已经加载, SavePolicy 方法会被禁用。

执行器

Casbin 使用配置文件来定义访问控制模型。

例如使用文件记录模型和策略存储文件,作为两个配置文件:model.confpolicy.csvmodel.conf 保存访问模型,而 policy.csv 保存具体的用户权限配置。Casbin 的使用非常简单。我们只需创建一个主要结构:enforcer。当构造这个结构的时候,model.confpolicy.csv 将会被加载。

换句话说,要创建一个 Casbin 执行器,您需要提供一个模型和一个适配器。

Casbin 提供了一个 FileAdapter,您可以使用它。更多信息,请参阅适配器。

使用模型文件和默认 FileAdapter 的示例:

1
2
3
import "github.com/casbin/casbin/v2"

e, err := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")

在 Casbin 中,执行器(Enforcer)是负责执行访问控制策略的核心组件。它提供了一系列方法,用于判断主体是否被授予对特定资源执行特定操作的权限。

执行器的主要功能包括:

  1. 加载策略:执行器可以从不同的策略存储(如文件、数据库)中加载访问控制策略。它可以读取和解析策略规则,并将其转换为内部模型进行处理。

  2. 决策判断:执行器可以根据加载的策略和传入的访问请求,进行访问控制决策。它会根据模型中定义的规则和策略,判断主体是否有权限执行特定的操作。

  3. 策略管理:执行器提供了管理策略的方法,可以动态地添加、修改或删除策略规则。这使得开发人员可以根据需要对访问控制策略进行灵活的调整和管理。

  4. 角色管理:执行器支持角色(Role)的管理,包括角色的添加、删除和关联。角色可以用于实现基于角色的访问控制(RBAC)模型。

通过执行器,开发人员可以轻松地集成 Casbin 的访问控制功能到他们的应用程序中。执行器提供了简单而强大的 API,使开发人员能够方便地进行访问控制决策和策略管理。

几种执行器

https://casbin.org/zh/docs/enforcers

  • 通用执行器:最基础的执行器,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从存储中加载策略规则 (aka LoadPolicy()) 或者将策略规则保存到其中 (aka SavePolicy())。 为了保持代码轻量级,我们没有把adapter代码放在主库中。

Casbin的适配器完整列表如下。

https://casbin.org/zh/docs/adapters

备注

  1. 如果使用显式或隐式adapter调用casbin.NewEnforcer(),策略将自动加载。
  2. 可以调用e.LoadPolicy() 来从存储中重新加载策略规则。
  3. 如果adapter不支持Auto-Save特性,则在添加或删除策略时不能将策略规则自动保存回存储器。 你必须手动调用 SavePolicy() 来保存所有的策略规则

监视器

casbin 支持使用分布式消息系统(如 etcd)来保持多个 Casbin 执行器实例之间的一致性。这样,用户就可以同时使用多个 Casbin 执行器来处理大量权限检查请求。

与策略存储适配器类似,casbin 在主库中不包含监视器代码。任何对新消息系统的支持都应该作为监视程序来实现。 Casbin 监视器的完整列表如下。

https://casbin.org/zh/docs/watchers

推荐使用 Redis ,内部是通过 redis 的 channel 实现多个实例之间通讯。

调度器

调度程序提供了一种同步策略增量变化的方法。它们应基于 Raft 等一致性算法,以确保所有执行者实例的一致性。通过调度器,用户可以轻松建立分布式集群。

调度器的方法分为两部分。 第一部分是与 Casbin 结合的方法。这些方法应在 Casbin 内部调用。用户可以使用 Casbin 本身提供的更完整的 API。

另一部分是调度器自身定义的方法,包括调度器初始化方法和不同算法提供的不同功能,如动态成员资格和配置更改。

我们希望调度器只在运行时确保 Casbin 执行器的一致性。因此,如果策略在初始化时不一致,调度程序将无法正常工作。用户需要在使用调度程序前确保所有实例的状态一致。

下面是 Casbin 调度程序的完整列表。欢迎任何第三方提供新的调度程序。请通知我们,我们将把它添加到此列表中。

https://casbin.org/zh/docs/dispatchers

overall architecture

调度器的使用场景更合适分布式的环境下。

角色管理器

角色管理器用于管理 Casbin 中的 RBAC 角色层次结构(用户角色映射)。 角色管理器可从 Casbin 策略规则或外部来源(如 LDAP、Okta、Auth0、Azure AD 等)检索角色数据。我们支持不同的角色管理器实现。 为了保持轻量级,我们在主库中不包含角色管理器代码(默认角色管理器除外)。

下面提供了 Casbin 角色管理器的完整列表。

https://casbin.org/zh/docs/role-managers

使用场景介绍

casbin 的 API 比较简单,基本上在官网直接搜索就可以找到。例如经常使用的一个场景是 后端 使用 MySQL 或者 PostgreSQL 存储策略,模型使用 RBAC 或者 基于域的 RBAC 。

注意点:

  1. 手动增加权限时,需要注意参数位置:

    例如模型定义:

    1
    2
    [policy_definition]
    p = sub, dom, obj, act

    添加权限时

    1
    2
    // sub, dom, obj, act
    _, err := r.e.AddPermissionForUser(portalRole.RoleID, portalId, portalId, portalRole.PermissionID)

    依次是:主体(角色或者用户id)、域、对象、操作

  2. 使用 redis 同步的时候,插入新的策略,在其他的实例上消费 redis 的消息时,再次插入会报错唯一键冲突。寻求解决方法过程比较惨:

    1. 去掉自动保存。这种方案不会有主键冲突,但是无法做到接受请求的实例保存而其他消费实例不保存到数据库的需求,除非自己再加一个锁,消费的时候加锁,修改自动保存设置,消费结束释放锁,然后作为服务实例接受请求的时候,加锁处理。这种方式很繁琐。
    2. 去掉自动保存会出现更大的问题,增加的策略都保存在内存中,而不是在数据库中
    3. 监视器不使用增量,获取到变更时加载数据库。这种方案很重量,如果策略很多,这个操作会很耗时,而且对内存和数据库都有不小的开销。casbin 是直接将数据库的所有数据都一次性读取到内存中。

    最终解决方法是使用 PostgreSQL 作为适配器时,如果冲突,则 DoNothing

    1
    2
    3
    db = db.Clauses(clause.OnConflict{
    DoNothing: true,
    })
  3. 提升性能的小技巧:通过调整 matchers ,可以加快匹配速度。

    例如:

    1
    2
    [matchers]
    m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

    将函数等比较耗时的表达式放在后面,执行时间就会很短。

    1
    2
    [matchers]
    m = r.obj == p.obj && g(r.sub, p.sub) && r.act == p.act

参考

casbin官方文档

casbin

enforcer_sync

Gorm Adapter

Redis Watcher