菜单权限
本页面展示了一个使用 jCasbin 实现基于菜单的访问控制的 Spring Boot 示例。同样的模式(policy + 角色分配 + 菜单层级)也可以应用到 Casbin 支持的其他语言中。
1. 配置
在 policy.csv 中定义角色—菜单权限、用户—角色分配以及菜单层级。完整示例:jCasbin menu permission。
1.1 概览
policy 文件定义了:(1) 哪些角色可以访问哪些菜单项,(2) 哪些用户拥有哪些角色,(3) 菜单项之间的父子关系。这些组合起来就实现了基于角色的、带层级的菜单可见性。
1.2 权限规则(p)
以 p 开头的行定义了某个角色(sub)是否可以对某个菜单项(obj)执行某个操作(act)。effect(eft)为 allow 或 deny。
示例:
p, ROLE_ROOT, SystemMenu, read, allow授予ROLE_ROOT对SystemMenu的 read 权限。p, ROLE_ROOT, UserMenu, read, deny拒绝ROLE_ROOT对UserMenu的 read 权限。
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 示例 policy
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
| MenuName | ROLE_ROOT | ROLE_ADMIN | ROLE_USER |
|---|---|---|---|
| SystemMenu | ✅ | ❌ | ❌ |
| UserMenu | ❌ | ✅ | ❌ |
| UserSubMenu_allow | ❌ | ✅ | ✅ |
| UserSubSubMenu | ❌ | ✅ | ✅ |
| UserSubMenu_deny | ❌ | ✅ | ❌ |
| AdminMenu | ✅ | ✅ | ❌ |
| AdminSubMenu_allow | ✅ | ✅ | ❌ |
| AdminSubMenu_deny | ✅ | ❌ | ❌ |
2. 菜单权限的执行
在 jCasbin menu permission 示例中,MenuService 提供了 findAccessibleMenus()(用户可以看到的所有菜单)和 checkMenuAccess()(检查单个菜单)。二者底层都使用 jCasbin enforcer。