Skip to content
Go
安装与快速开始

安装与快速开始

Casbin 是一个强大、高效的开源访问控制框架,支持 ACL、RBAC、ABAC 等多种访问控制模型。它的核心思想是:把"权限模型"和"权限数据"从业务代码中剥离出来——模型用一个 .conf 文件描述,数据(策略)存在 CSV/数据库中,业务代码只需要问一句话:

某个主体(sub)能否对某个资源(obj)执行某个操作(act)?

ok, err := e.Enforce("alice", "data1", "read") // alice 能否读 data1?

Casbin 是什么 / 不是什么

Casbin 负责 Casbin 不负责
权限判定(authorization):谁能对什么做什么 身份认证(authentication):验证用户是谁、密码/token 校验
存储和管理访问策略(角色、权限映射) 用户/密码/session 管理
多种模型:ACL、RBAC、ABAC、RESTful… 加密、签名

💡 最佳实践:Casbin 应该放在认证之后 —— 先用 JWT/Session 确认"你是 alice",再用 Casbin 回答"alice 能不能做这件事"。两者是流水线关系,不是竞争关系。

安装

⚠️ 2025 年起 Casbin 进入 Apache 孵化器(仓库为 apache/casbin),主版本升级到 v3,Go 模块路径为 github.com/casbin/casbin/v3。v3 的核心 API 与 v2 兼容,旧文章中的 casbin/v2 示例通常只需改 import 即可运行。

go get github.com/casbin/casbin/v3

常用生态库(按需安装):

# GORM 数据库适配器(策略存 MySQL/PostgreSQL/SQLite/SQL Server)
go get github.com/casbin/gorm-adapter/v3

# Redis Watcher(多实例策略同步)
go get github.com/casbin/redis-watcher/v2
模块路径 作用
核心 github.com/casbin/casbin/v3 Enforcer、模型解析、策略求值
GORM 适配器 github.com/casbin/gorm-adapter/v3 策略持久化到关系型数据库
Redis Watcher github.com/casbin/redis-watcher/v2 多实例间策略变更通知

三个核心文件/对象

使用 Casbin 只需要理解三样东西:

┌─────────────────┐   ┌─────────────────┐
│  model.conf     │   │  policy.csv     │
│  (权限模型)     │   │  (权限数据)     │
│  "规则怎么算"     │   │  "谁有什么权限"   │
└────────┬────────┘   └────────┬────────┘
         └──────────┬──────────┘
                    ▼
            ┌──────────────┐
            │   Enforcer   │ ← e.Enforce(sub, obj, act)
            └──────────────┘
  1. Model(模型).conf 文件,定义请求长什么样、策略长什么样、怎么匹配 —— 详见 01-核心概念
  2. Policy(策略):具体的权限条目,可以是 CSV 文件、数据库表 —— 详见 05-适配器与持久化
  3. Enforcer(执行器):加载模型 + 策略,对外提供 Enforce() 判定接口

第一个例子:ACL

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

含义逐行拆解:

内容 白话解释
request_definition r = sub, obj, act 每次询问带三个参数:主体、资源、操作
policy_definition p = sub, obj, act 每条策略也是三元组
policy_effect some(where (p.eft == allow)) 只要有任意一条策略匹配成功就放行
matchers r.sub == p.sub && ... 请求和策略三个字段完全相等才算"匹配"

policy.csv

p, alice, data1, read
p, bob, data2, write

含义:alice 可以读 data1;bob 可以写 data2。

main.go

package main

import (
	"fmt"
	"log"

	"github.com/casbin/casbin/v3"
)

func main() {
	e, err := casbin.NewEnforcer("model.conf", "policy.csv")
	if err != nil {
		log.Fatalf("创建 enforcer 失败: %v", err)
	}

	// 判定:注意 Enforce 返回 (bool, error) 两个值
	ok, err := e.Enforce("alice", "data1", "read")
	if err != nil {
		log.Fatalf("判定出错: %v", err)
	}
	fmt.Println(ok) // true

	ok, _ = e.Enforce("alice", "data1", "write")
	fmt.Println(ok) // false —— 没有任何策略允许 alice 写 data1
}

🚨 陷阱Enforce 返回的 error 不能忽略。模型语法错误、参数个数与 request_definition 不一致时会返回 error(此时 ok 恒为 false),如果只看 ok 会把"配置写错了"误判成"没有权限",非常难排查。

第二个例子:RBAC(最常用)

实际项目几乎都用 RBAC:用户 → 角色 → 权限,权限挂在角色上,用户只挂角色。

model.conf

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

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

相比 ACL 多了两处:

  • [role_definition]g = _, _ 声明了一个角色继承关系(两个参数:用户, 角色)
  • matcher 中 r.sub == p.sub 换成 g(r.sub, p.sub):请求主体 p.sub 这个角色(或继承了它)即可

policy.csv

p, admin, data, read
p, admin, data, write
p, viewer, data, read

g, alice, admin
g, bob, viewer
  • p 行:角色的权限(admin 可读写 data,viewer 只能读)
  • g 行:用户与角色的绑定(alice 是 admin,bob 是 viewer)

main.go

e, _ := casbin.NewEnforcer("model.conf", "policy.csv")

ok, _ := e.Enforce("alice", "data", "write") // true  —— alice ∈ admin
ok, _ = e.Enforce("bob", "data", "write")    // false —— viewer 无 write
ok, _ = e.Enforce("bob", "data", "read")     // true

// 运行时管理角色(详见 03-RBAC与多租户)
e.AddRoleForUser("carol", "viewer")
roles, _ := e.GetRolesForUser("alice") // [admin]

从字符串加载模型(不依赖 .conf 文件)

模型也可以内嵌在代码里,适合不想多带一个配置文件的场景:

import "github.com/casbin/casbin/v3/model"

m, err := model.NewModelFromString(`
[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
`)
if err != nil {
	log.Fatal(err)
}
e, err := casbin.NewEnforcer(m) // 只有模型、没有策略,之后用 AddPolicy 添加

💡 最佳实践:生产项目常见组合是 模型内嵌字符串(或 embed)+ 策略存数据库。模型属于代码逻辑的一部分,跟随版本控制;策略是运行时数据,跟随数据库。

//go:embed model.conf
var modelText string // 用 go:embed 兼得两者:模型独立成文件,又编译进二进制

在线调试神器:Casbin Editor

强烈推荐https://casbin.org/editor/

左边写 model,右下写 policy,上面输入 request,实时看到判定结果。调试 matcher 表达式、验证模型设计时,先在 Editor 里试通再落到代码里,效率远高于反复改代码重启。

推荐项目结构

myapp/
├── internal/
│   └── authz/
│       ├── model.conf        # 权限模型(embed 进二进制)
│       ├── enforcer.go       # 初始化 Enforcer(单例)
│       └── middleware.go     # Gin/gRPC 中间件(见 07-Web框架集成)
└── ...
// enforcer.go —— 全局单例,进程内只初始化一次
package authz

import (
	_ "embed"
	"sync"

	"github.com/casbin/casbin/v3"
	"github.com/casbin/casbin/v3/model"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	"gorm.io/gorm"
)

//go:embed model.conf
var modelText string

var (
	enforcer *casbin.SyncedEnforcer
	once     sync.Once
)

func Init(db *gorm.DB) (*casbin.SyncedEnforcer, error) {
	var err error
	once.Do(func() {
		var m model.Model
		var a *gormadapter.Adapter
		m, err = model.NewModelFromString(modelText)
		if err != nil {
			return
		}
		a, err = gormadapter.NewAdapterByDB(db)
		if err != nil {
			return
		}
		// Web 服务用 SyncedEnforcer(并发安全),见 08-分布式与高性能
		enforcer, err = casbin.NewSyncedEnforcer(m, a)
	})
	return enforcer, err
}

学习路径(本手册目录)

文件 内容 建议
01-核心概念 PERM 元模型、model.conf 语法详解 必读,理解一切的基础
02-访问控制模型大全 ACL/RBAC/ABAC/RESTful/优先级 全部模型 按需查阅,选型时通读
03-RBAC与多租户 角色继承、domains、RBAC API 用 RBAC 必读
04-策略管理API 增删改查策略的完整 API 工具书
05-适配器与持久化 数据库存储、gorm-adapter、过滤加载 上生产必读
06-匹配器与函数 keyMatch 系列、自定义函数、eval RESTful/ABAC 必读
07-Web框架集成 Gin 中间件、JWT、gRPC 实战
08-分布式与高性能 Watcher、缓存、并发安全 多实例部署必读
09-最佳实践与陷阱 全手册陷阱汇总、生产检查清单 上线前过一遍

常见陷阱

🚨 import 路径写错版本:v3 必须写 github.com/casbin/casbin/v3,写成 github.com/casbin/casbin 会拉到远古的 v1。网上大量旧教程是 /v2,API 基本兼容,但新项目应直接用 /v3

🚨 忽略 Enforce 的 error:参数个数与 request_definition 不符、matcher 写错字段名等配置错误都通过 error 报告,只看 bool 会把配置错误当成"无权限"。

🚨 CSV 策略文件格式敏感:每行以 p,g, 开头;字段值本身含逗号时必须用双引号包裹(如 p, alice, /api/*, "(GET)|(POST)");行尾多余空格可能导致匹配失败("read ""read")。

🚨 策略字段数必须与 policy_definition 一致p = sub, obj, act 却写了 4 个字段的策略行,加载时会报错或产生难以察觉的错位。

🚨 文件 adapter 只适合 demo:CSV 文件不支持 AutoSave、无并发保护,生产环境请用数据库 adapter(见 05-适配器与持久化)。