Menu Permissions
本页展示一个用 jCasbin 实现基于菜单的访问控制的 Spring Boot 示例。同样的模式(策略 + 角色分配 + 菜单层级)也可以用在 Casbin 支持的其他语言中。
1. 配置
在 policy.csv 中定义角色与菜单的权限、用户与角色的分配,以及菜单层级。完整示例:jCasbin 菜单权限。
1.1 概述
策略文件定义了:(1) 哪些角色可以访问哪些菜单项;(2) 哪些用户拥有哪些角色;(3) 菜单项之间的父子关系。这三者结合,就实现了基于角色、带层级的菜单可见性。
1.2 权限规则(p)
以 p 开头的行定义某个角色(sub)能否对某个菜单项(obj)执行某个操作(act)。效果(eft)为 allow 或 deny。
示例:
p, ROLE_ROOT, SystemMenu, read, allow授予ROLE_ROOT对SystemMenu的读权限。p, ROLE_ROOT, UserMenu, read, deny拒绝ROLE_ROOT对UserMenu的读权限。
1.3 用户与角色的分配(g)
以 g 开头的行把用户分配到角色,并定义角色继承。用户获得其所有角色权限的并集。
示例:
g, user, ROLE_USER把名为user的用户分配到ROLE_USER。g, ROLE_ADMIN, ROLE_USER让ROLE_ADMIN继承ROLE_USER的所有权限。
1.4 菜单层级(g2)
以 g2 开头的行定义菜单的父子结构。
示例:
g2, UserSubMenu_allow, UserMenu让UserSubMenu_allow成为UserMenu的子菜单。g2, (NULL), SystemMenu把SystemMenu标记为没有父菜单的顶级菜单。
1.5 菜单权限继承
- 如果 父 菜单显式设置了
allow,其 子 菜单会继承allow,除非子菜单显式设置了deny。所以允许父菜单就等于允许它的子菜单。 - 如果父菜单没有显式权限,但至少有一个子菜单是
allow,父菜单会被视为allow,让用户能访问到那个子菜单。
1.6 拒绝与角色继承
- 显式拒绝 优先:如果某个角色被显式拒绝访问某个菜单,所有继承它的角色也会被拒绝。拒绝不会因继承而被覆盖。
- 隐式拒绝(没有
allow规则)可以被覆盖:权限更高的角色可以为该菜单添加显式的allow。
1.7 策略示例
p, ROLE_ROOT, SystemMenu, read, allow
p, ROLE_ROOT, AdminMenu, read, allow
p, ROLE_ROOT, UserMenu, read, deny
p, ROLE_ADMIN, UserMenu, read, allow
p, ROLE_ADMIN, AdminMenu, read, allow
p, ROLE_ADMIN, AdminSubMenu_deny, read, deny
p, ROLE_USER, UserSubMenu_allow, read, allow
g, user, ROLE_USER
g, admin, ROLE_ADMIN
g, root, ROLE_ROOT
g, ROLE_ADMIN, ROLE_USER
g2, UserSubMenu_allow, UserMenu
g2, UserSubMenu_deny, UserMenu
g2, UserSubSubMenu, UserSubMenu_allow
g2, AdminSubMenu_allow, AdminMenu
g2, AdminSubMenu_deny, AdminMenu
g2, (NULL), SystemMenu
| 菜单名称 | ROLE_ROOT | ROLE_ADMIN | ROLE_USER |
|---|---|---|---|
| SystemMenu | ✅ | ❌ | ❌ |
| UserMenu | ❌ | ✅ | ❌ |
| UserSubMenu_allow | ❌ | ✅ | ✅ |
| UserSubSubMenu | ❌ | ✅ | ✅ |
| UserSubMenu_deny | ❌ | ✅ | ❌ |
| AdminMenu | ✅ | ✅ | ❌ |
| AdminSubMenu_allow | ✅ | ✅ | ❌ |
| AdminSubMenu_deny | ✅ | ❌ | ❌ |
2. 执行菜单权限
在 jCasbin 菜单权限 示例中,MenuService 提供 findAccessibleMenus()(用户能看到的所有菜单)和 checkMenuAccess()(检查单个菜单)。两者底层都使用 jCasbin 的 enforcer。