跳转至主要内容

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_ROOTROLE_ADMINROLE_USER
SystemMenu✅❌❌
UserMenu❌✅❌
UserSubMenu_allow❌✅✅
UserSubSubMenu❌✅✅
UserSubMenu_deny❌✅❌
AdminMenu✅✅❌
AdminSubMenu_allow✅✅❌
AdminSubMenu_deny✅❌❌

2. 执行菜单权限​

在 jCasbin 菜单权限 示例中,MenuService 提供 findAccessibleMenus()(用户能看到的所有菜单)和 checkMenuAccess()(检查单个菜单)。两者底层都使用 jCasbin 的 enforcer。