角色菜单授权
角色菜单授权回答一个后台管理系统最常见的问题:某个角色可以看到哪些菜单、进入哪些页面、使用哪些按钮、调用哪些接口,以及接口响应里能拿到哪些字段。
它不会自动绑定用户。流程是先给角色保存菜单授权,再用 userRoles.assign() 或 userRoles.set() 把角色交给用户。
后台授权页最小流程
如果你正在做一个后台“角色授权”页面,先按这个顺序想:
普通后台页面最常见的是“保存整棵授权树”,也就是读取 getAuthorizationTree(),用户勾选后组装 assignments,调用 preview({ operation: 'set', assignments }),确认后再调用 set()。
对象怎样连在一起
一条主线是:
菜单配置 → 角色菜单授权 → 用户角色绑定 → 当前用户运行时投影
不要把 MenuConfigInput 当成“权限结果”。它只是可被授权的资源目录。真正决定用户能不能访问的是角色授权和用户角色绑定。
后台勾选如何变成 selection
selection 可以理解成“这次后台表单勾了哪些能力”。不用先记住完整类型,先记住这张映射表:
构造选择
下面的选择表示:给 order-operator 角色分配 admin 配置里的订单列表页;页面加载接口和页面按钮也一起授权;订单列表接口只允许返回 orderNo 和 status 两个字段。
默认值是:descendants: false、loads: true、actions: false、responseFields: 'none'。也就是说,勾选一个页面时会默认给页面加载接口,但不会默认给按钮,也不会默认给响应字段。如果你希望“选中页面时默认拥有所有已声明响应字段”,可以把 include.responseFields 设为 'all'。后台管理系统一般更建议显式选择字段,避免页面后来新增敏感字段时自动泄漏给旧角色。
分页响应或多层响应要写 target。例如接口返回 { items, total } 时,target: 'items' 表示授权的是 items 每一行里的字段;total 这类分页字段应在响应配置的 preserve 中声明,不要放进角色字段授权。
预览再提交
和前面创建菜单、页面、接口不同,角色菜单授权会真正改变某个角色的访问范围,可能立刻影响很多用户。因此这里仍然要求显式 preview,再把同一份选择和 preview 凭证交给写入方法。普通管理员不用理解 revision 细节,只要记住:授权页先预览影响,确认后提交。
先预览:
无冲突时,管理端重点看 executable: true、影响用户数量和本次会生成的授权内容。返回结构可以理解成这样:
确认保存时,把同一个 assignments 和 preview 返回的凭证传给写入方法:
menuPermissions.preview(roleId, change) 不写数据库,只计算这次授权会生成多少来源、影响哪些用户、是否有冲突。menuPermissions.set(roleId, assignments, options) 才把后台当前勾选结果写成该角色的完整直接菜单授权。执行时必须传入预览返回的 expected 和 previewToken。
inserted/updated/deleted/unchanged/conflicted 是本次保存的批量写入摘要;samples.items 只给排查用的少量样例,不是授权判断 API。想看更细的授权来源、响应字段和影响用户,应优先看 preview 的 summary。
授权、拒绝、撤销和替换
这四个方法不是平级心智。普通后台授权页面优先使用 set() 保存整棵授权树;其他方法更多用于追加、覆盖或精确删除。
set() 只替换菜单授权,不替换手工 roles.allow() / roles.deny() 规则,也不修改用户绑定了哪些角色。每个写方法都要先用对应 operation 预览。
读取角色授权
精确返回类型见角色菜单权限 API。这里先记住:getAuthorizationTree() 面向管理员编辑界面,不是用户侧菜单树。用户侧菜单树使用 subject.menus.getViewTree({ configId })。
用户端运行时结果
验收菜单和按钮
授权保存后,如果要验证某个用户最终能看到什么,先把角色绑定给用户,再用用户 subject 读取菜单树和按钮状态:
裁剪接口响应
业务接口返回前,用同一个 subject 裁剪响应字段:
filterResponse(apiResource, payload) 会先检查当前用户是否拥有 invoke + apiResource,再根据该用户的响应字段授权裁剪数据;裁剪后的业务 payload 在 projected.data。没有授权的字段会被移除;没有接口调用权限时会拒绝,而不是返回未裁剪数据。
角色、用户、菜单的边界
完整示例见菜单管理示例,精确签名见角色菜单权限 API。