第 1 章 权限模型总览:RBAC 与 Casbin
本文导航

第 1 章 权限模型总览:RBAC 与 Casbin
这一章建立整体地图。先记住五个对象和它们的关系,后面的接口与操作都只是在这张图上做增删改。
五个对象
| 对象 | 代码 | 说明 |
|---|---|---|
| 用户 | model.User |
登录主体,可拥有多个角色 |
| 角色 | model.Role |
权限集合,可拥有多个菜单 |
| 菜单 | router.Menu |
页面菜单或按钮菜单;按钮携带权限标识 |
| 路由 | router.RouterInfo |
运行时注册的接口,带访问级别与权限标识 |
| 策略 | casbin_rule 表 |
p 行是角色→权限,g 行是用户→角色 |
关系:
admin_user ──< admin_user_role >── admin_role
│
└──< admin_role_menu >── 菜单 ID
│
└── 权限标识
│
casbin_rule (p)
│
admin_user ──────────────────────────────── casbin_rule (g) ───────────┘
菜单是权限的载体
菜单有两种:
- 页面菜单(
MenuTypeMenu/MenuTypeDir):控制前端路由与侧栏显示; - 按钮菜单(
MenuTypeBtn):不显示,只用来承载权限标识。
RouterInfoBuilder.Build() 会为每条 AccessAuthorized 路由自动生成一个按钮菜单,名字就是 Handler 方法名,标题是 Name("创建用户") 里的中文:
{
Name: "Create",
Meta: router.Meta{Title: "创建用户"},
MenuType: router.MenuTypeBtn,
DependentsPermission: []string{
"admin:user:Create",
},
}
所以「给角色分配菜单」实际上是在选「这个角色能做哪些页面和按钮」。分配后,菜单上的 DependentsPermission 会被抽出来写进 Casbin。
Casbin 模型
owl/provider/permission/casbin.go 里写死了模型:
[request_definition]
r = sub, act
[policy_definition]
p = sub, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.act == p.act
含义:
sub是用户 ID,act是权限标识;p, <roleID>, <permission>表示角色拥有某权限;g, <userID>, <roleID>表示用户属于某角色;- 匹配时,只要用户属于某个拥有该权限的角色,就放行。
数据库里对应 casbin_rule 表:
| ptype | v0 | v1 |
|---|---|---|
| p | 761298100 | admin:user:Create |
| g | 761298200 | 761298100 |
请求链路
POST /api/v1/users
Authorization: Bearer <token>
↓
PermissionCheck 中间件
↓
1. 用方法和路径在运行时路由表里找到路由
2. 读取 AccessLevel
3. AccessPublic:直接放行
4. 其余:解析 JWT
5. 超管:直接放行
6. AccessAuthenticated:令牌有效即可
7. AccessAuthorized:enforcer.Enforce(userID, permission)
8. AccessSuperAdmin:非超管拒绝
中间件定义在 owl-admin/app/middleware/permission_check.go,挂载在 /api/v1 分组上。
超管为什么不受限
配置里的超管不是数据库用户。登录时如果用户名命中 conf/app.yaml 的 admin.username,服务会构造一个内置用户:
func NewSuperUser() User {
return User{
BaseModel: db.BaseModel{ID: 19941996},
Username: "glen",
Nickname: "超级管理员",
IsSuperAdmin: true,
Permissions: []string{"*:*:*"},
Roles: []Role{{Name: "superAdmin"}},
}
}
中间件看到 IsSuperAdmin 就直接 Next(),不查 Casbin;/users/me/permissions 也直接返回 *:*:*。
一个完整例子
假设:
- 角色
运营(ID1001); - 菜单「用户管理」下勾选了「创建用户」按钮;
- 用户
demo(ID2001)属于运营。
则:
p, 1001, admin:user:Create
g, 2001, 1001
demo 请求 POST /api/v1/users:
- 路由表找到
admin:user:Create; Enforce("2001", "admin:user:Create")为 true;- 放行。
如果把「创建用户」按钮从角色菜单里取消:
- 监听器先删除角色
1001的全部 policy; - 再按剩余菜单写入新 policy;
- 下一次请求
Enforce为 false,返回 403。
自检
- [ ] 能画出用户、角色、菜单、路由、Casbin 的关系图;
- [ ] 能说出按钮菜单的权限标识从哪里来;
- [ ] 能解释
p行和g行分别代表什么; - [ ] 能说明超管为什么不查 Casbin。
暂无上一篇
下一篇 第 2 章 登录与 JWT:令牌怎么来、怎么用
评论
加载评论中…