# 对象权限控制

平台用 角色要求(RoleRequirement)控制谁可以查看、创建、修改、删除某类对象,以及谁可以访问某个表单、动作、菜单。需要根据对象本身的字段值做判断时,可以在角色要求上挂一段自定义逻辑。

提示

1.0 起已移除基于 Request Map 的权限配置,以及表单上的 enable roles 字段,统一改用本页介绍的角色要求。

# 基础权限控制

# 角色要求

一个角色要求(RoleRequirement)包含以下字段:

属性名 类型 说明
name String 名称,租户内唯一,创建后不可修改
hasPermissionRoles List<HierarchyRole> 满足要求的角色列表,用户拥有其中任意一个角色即满足
customLogic DynamicLogic 自定义判断逻辑,可以为空,见 动态权限

判断规则:

  • 设置了 customLogic 时,只按自定义逻辑的结果判断,hasPermissionRoles 不再起作用;
  • 否则判断用户是否拥有 hasPermissionRoles 中的任一角色。角色可以继承,例如平台内置的 ROLE_ADMIN 包含 ROLE_DEVELOPER,ROLE_DEVELOPER 包含 ROLE_USER,所以拥有 ROLE_ADMIN 的用户也满足只要求 ROLE_USER 的角色要求。

平台内置三个角色要求:

名称 满足要求的角色
USER ROLE_USER
DEVELOPER ROLE_DEVELOPER
ADMIN ROLE_ADMIN

# 领域模型的增删改查权限

每个领域模型(DomainClass)上有四个角色要求字段,分别控制四种操作:

属性名 控制的操作
createRoleRequirement 创建
readRoleRequirement 查看
updateRoleRequirement 修改
deleteRoleRequirement 删除

注意

某个操作没有配置角色要求时,任何人(包括管理员)都不能执行该操作。例如领域模型没有配置 readRoleRequirement,通过 /data/<领域模型> 查询会返回 403 NoPermission。

这四个字段在调用平台的数据接口(/data/...)时由后端校验,也决定了界面上是否显示新建、修改、删除按钮。

# 表单、动作、菜单的访问权限

表单(DynamicForm)、对象动作(DynamicAction)、菜单(DynamicMenu)上各有一个 accessRequirement(访问要求)字段,未配置时的行为不同:

对象 配置了 accessRequirement 未配置
表单 满足要求才能打开和读取该表单 不做额外限制(仍需登录,并受领域模型查看权限约束)
菜单 满足要求才显示该菜单 表单类菜单:显示(仍受所指向表单的要求限制);分组菜单、链接菜单:不显示
对象动作 满足要求才显示、才能执行 任何人都不能执行

# 通过种子数据配置

当前版本的界面上没有编辑角色要求的入口,领域模型、表单、动作、菜单的表单里也没有这些字段。请在插件或种子数据的 CSV 中配置:

RoleRequirement.csv:定义角色要求。列名 hasPermissionRoles.name[:] 中的 [:] 表示多个角色之间用英文冒号 : 分隔,写成 [ROLE_A:ROLE_B],满足其中任意一个角色即可。不要用逗号写成 [ROLE_A,ROLE_B],逗号是 CSV 的列分隔符,会把这一行拆坏。

name(*),hasPermissionRoles.name[:],customLogic.name

EQUIPMENT_ADMIN,ROLE_EQUIPMENT_ADMIN,
REPORT_VIEWER,[ROLE_USER],
EQUIPMENT_MANAGER,[ROLE_EQUIPMENT_ADMIN:ROLE_ADMIN],
1
2
3
4
5

其中 ROLE_EQUIPMENT_ADMIN 不是平台内置角色,要先在 HierarchyRole.csv 里定义(例如 ROLE_EQUIPMENT_ADMIN,[ROLE_USER]),写法见 数据导入。

DomainClass*.csv:给领域模型指定四种操作的角色要求。

shortName(*),extInfo,createRoleRequirement.name,readRoleRequirement.name,updateRoleRequirement.name,deleteRoleRequirement.name

EquipmentProfile,,EQUIPMENT_ADMIN,USER,EQUIPMENT_ADMIN,EQUIPMENT_ADMIN
1
2
3

DynamicForm*.csv、DynamicMenu*.csv、DynamicAction*.csv:用 accessRequirement.name 列指定访问要求,例如

name(*),label,parent.name,icon,link,type,displaySequence,form.name,accessRequirement.name
1

CSV 的通用写法见 数据导入。

# 动态权限

需要根据对象的字段值、当前用户等信息动态判断权限时,给角色要求设置 customLogic。

# 自定义逻辑的注入变量

变量名称 变量类型 描述
object Object 被检查的对象,随使用场景不同,见下方说明
hasPermissionRoles List<HierarchyRole> 该角色要求上配置的角色列表
user tech.muyan.api.security.MuyanAuthentication 当前用户,user.authorities*.authority 可取得角色名列表

逻辑返回 [result: true] 表示满足要求,返回 [result: false] 或不返回 result 表示不满足。

示例:只有开发者可以修改 Webhook,但名为“接收订单通知”的 Webhook 谁都不能改。

boolean isDeveloper = user.authorities*.authority.contains('ROLE_DEVELOPER')
return [result: isDeveloper && object?.name != '接收订单通知']
1
2

object 的取值:

角色要求用在 object
表单 / 动作 / 菜单的 accessRequirement 该表单(DynamicForm)/ 动作(DynamicAction)/ 菜单(DynamicMenu)
自定义 Controller 的 roleRequirement 当前请求(HttpServletRequest)
领域模型的四个角色要求 见下方说明,可能是具体对象,也可能是 null

领域模型角色要求中的 object

object 只有在按对象查询权限时(即下文的 权限查询接口 POST /permissions/<领域模型>/,前端据此决定每一行是否显示修改、删除按钮)才是具体的对象。后端在处理 /data/... 增删改查请求时,检查的是“能否操作这类对象”,传入的 object 为 null。

实测:用上面的示例逻辑作为 DynamicWebhook 的 updateRoleRequirement,权限查询接口对“接收订单通知”返回 update: false,界面上不显示修改按钮;但直接调用 PUT /api/data/DynamicWebhook/<id> 仍然修改成功。所以对象级的判断目前只能控制界面,不能作为数据安全的保证;逻辑也必须能处理 object == null 的情况,否则所有增删改查请求都会被拒绝。

# 动态创建权限

除了在 createRoleRequirement 上挂自定义逻辑,还可以为领域模型创建一个 Create ability(CREATE)类型的对象钩子,决定当前用户能否创建该类对象。对象钩子在 开发定制 > 对象客制化 中维护,新建时这样填:

字段 填写
名称 钩子的名称,必填,创建后不可修改
类型 Create ability(选项当前显示为英文)
关联对象类型 该逻辑适用的领域模型
核心逻辑 具体的判断逻辑
是否生效中 必须打开。新建表单中默认是关闭的,关闭时保存的钩子不会执行
变量名称 变量类型 描述
objectType Class<?> 当前操作的对象类型
userContext tech.muyan.api.security.MuyanAuthentication 当前用户
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure<?> 用于打印执行日志的 log 闭包

逻辑要返回一个 result 键,其值是包含 create 键的 Map:

// 允许用户创建该对象,create 必须放在 result 中
// Allow user to create this object, "create" must be wrapped in "result"
return [result: [create: true]]
1
2

返回值格式

必须把 create 包在 result 里。直接返回 [create: true] 时,权限查询接口会报错 Cannot invoke "java.util.Map.get(Object)" because "result" is null。

说明:

  • 这个钩子只影响 权限查询接口 GET /permissions/<领域模型>/create 的结果,也就是界面上是否显示新建按钮;通过 POST /data/<领域模型> 创建对象时,后端仍只按 createRoleRequirement 校验。
  • 钩子返回 create: true,但用户不满足 createRoleRequirement 时,结果中的 create 仍为 true,另外带上 error: 1 和一段提示信息。界面上仍会显示新建按钮,但提交时会被后端拒绝。
  • 同一个领域模型配置了多个 CREATE 钩子时,全部忽略,按 createRoleRequirement 判断。

# 动态修改和删除权限

Update/delete ability(UPDATE_DELETE)类型的对象钩子当前不会被执行:界面上仍然可以选这个类型,但选了不起作用(实测权限查询结果不受其影响,也没有产生执行记录)。需要按对象判断修改、删除权限时,请在 updateRoleRequirement / deleteRoleRequirement 上使用 自定义逻辑,并注意上文关于 object 的限制。

# 权限查询接口

前端通过以下接口决定显示哪些按钮,也可以在自己的页面或第三方系统中调用(路径需加 /api 前缀,认证方式见 平台 API):

# 能否创建某类对象
curl http://<服务器地址>/api/permissions/DynamicWebhook/create \
  -H 'Authorization: Bearer <access_token>'
# {"create":true}

# 对一组对象的查看、修改、删除权限
curl -X POST http://<服务器地址>/api/permissions/DynamicWebhook/ \
  -H 'Authorization: Bearer <access_token>' -H 'Content-Type: application/json' \
  -d '{"ids":[1]}'
# {"1":{"view":true,"update":true,"delete":true}}
1
2
3
4
5
6
7
8
9
10
Last Updated: 2026/9/24 14:27:35