Payload 如何实现字段级权限:让不同角色只能访问文档的指定字段
【免费下载链接】payloadPayload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.项目地址: https://gitcode.com/GitHub_Trending/pa/payload
如果你的 Payload 项目中存在“管理员能看到全部字段,但普通编辑角色只能读取、修改文档中指定字段”的需求,只配置 Collection 级访问控制是不够的——它只能决定用户能否操作整篇文档,无法把权限细分到某个字段。Payload 的 Field-level Access Control 解决这个问题:在字段配置中声明access属性,用create、read、update三个函数分别控制“谁能写入该字段”“谁能读到该字段的值”“谁能修改该字段的值”。本文基于 Field-level Access Control 与 Access Control 总览 文档,给出配置方法、运行时行为差异和仓库内现成的验证方式。
先确认前提:集合级权限是字段级权限的前提
Payload 提供默认访问控制,无需额外配置即会生效(见 Access Control 总览):
const defaultPayloadAccess = ({ req: { payload, user } }) => { // Return `true` only if a user is found // and that user belongs to the admin user collection return Boolean(user) && user.collection === payload.config.admin.user }含义是:默认情况下只有属于config.admin.user指定集合(Admin Panel 用户集合)的登录用户才能执行操作;来自其他认证集合的用户默认被拒绝。因此字段级权限是在集合级权限放行之后、进一步细化到字段粒度的第二道规则。如果两个“角色”根本不属于同一集合,先要按 Admin Panel 的 RBAC 说明处理:在认证集合上加一个roles(或类似)字段,再用access.admin按该字段的值授予或拒绝 Admin Panel 访问权限。
在字段配置中声明 access 函数
给任意字段添加access属性即可(示例取自 Field Config 文档):
import type { CollectionConfig } from 'payload'; export const Posts: CollectionConfig = { slug: 'posts', fields: [ { name: 'title', type: 'text', access: { create: ({ req: { user } }) => { ... }, read: ({ req: { user } }) => { ... }, update: ({ req: { user } }) => { ... }, }, }; ], };三个函数的返回值与失败时的行为(以 fields 文档为准):
| 函数 | 控制的操作 | 返回false时的行为 |
|---|---|---|
create | 创建新文档时能否设置该字段的值 | 传入的值被直接丢弃 |
read | 能否读取该字段的值 | 该属性从返回的文档中整个省略 |
update | 能否更新该字段的值 | 不报错,该字段被跳过,原值保持不变 |
各函数可接收的参数包括:req(含当前认证用户user)、collection或global(字段所属集合/全局配置)、id、doc(读取/更新时文档的完整数据)、siblingData(同级字段数据)、blockData(字段位于 block 内时的父行数据);create额外提供data与siblingData。利用doc和siblingData,可以实现“根据文档其他字段决定某字段是否可见”这类规则。
示例:按角色限定字段的读取与修改
仓库文档中多处使用req.user?.roles?.includes('admin')这类判断(如 jobs-queue 概览、Collection Access 的 update 示例)。将同样的角色判断放进字段级access,即可实现“不同角色只能访问指定字段”:
{ name: 'title', type: 'text', access: { // 仅 admin 角色可读、可改该字段;其他角色读取时该字段被省略,更新时被静默丢弃 read: ({ req: { user } }) => Boolean(user?.roles?.includes('admin')), update: ({ req: { user } }) => Boolean(user?.roles?.includes('admin')), }, }如果只想让某个字段对所有角色只读,可以把create和update固定返回false、read返回true。仓库测试中 blocks 字段的配置就是这个模式(见 BlocksFieldAccess 测试集合):
access: { read: () => true, create: () => false, update: () => false, },两点注意:
- 字段级访问控制不支持返回 Query 约束,这与 Collection 级不同(fields 文档明确警告),只能返回布尔值。
- 对
group、array、blocks等容器字段声明access时,权限作用于整个容器:read: false时容器内的嵌套值一并从结果中消失。仓库集成测试验证了这一点:partiallyHiddenGroup/partiallyHiddenArray中隐藏的value字段对用户不可见(见下文“验证”)。
运行时必须知道的两个行为细节
来自 Access Control 总览:
- Access Operation 的执行上下文。登录时 Admin Panel 会通过 Access Operation 以顶层方式执行各集合、全局和字段的访问控制函数,此时
id、data、siblingData、blockData、doc参数都是undefined,且访问函数返回的 Where 查询不会被执行(直接视为无权限)。因此如果函数里使用了doc、id等参数,必须先判空,否则只应在用户已登录、上下文可用的路径下引用它们。 - Local API 默认跳过访问控制。通过
payload.create/payload.findByID等 Local API 操作时,所有 Access Control(包括字段级)默认被跳过,需要完整权限行为时设置overrideAccess: false。此外,顶层baseAccess配置只作用于 Collection 和 Global 级访问控制,不适用于字段级。
验证配置是否生效
仓库的集成测试 test/access-control/int.spec.ts 提供了可直接参照的验证方式,覆盖三种预期行为:
- 读取时被省略:普通
find/findByID返回的文档中,read: false的字段不存在。 - 更新时静默丢弃:
update请求即使携带被拒字段的值也不会抛错,字段保持原值。 - 隐藏值不受其他字段更新影响:测试先创建带隐藏字段值的文档,只更新
title,再用showHiddenFields: true读取原值确认未被篡改:
const updatedDoc = await payload.findByID({ id: doc.id, collection: hiddenFieldsSlug, showHiddenFields: true, }) // 文档中的示例断言(示例结果) expect(updatedDoc.partiallyHiddenGroup.value).toStrictEqual('private_value') expect(updatedDoc.partiallyHiddenArray[0].value).toStrictEqual('private_value')注意showHiddenFields: true是测试侧用于“看见”被隐藏字段的手段,用于核对数据确实仍在库中、只是对受限用户不可见。你自己的项目里,等价做法是:分别用 admin 角色和受限角色登录请求同一文档,对比响应 JSON 中被限制字段的有无;再对受限角色发起一次携带该字段值的update请求,确认返回不报错且字段值未变。
边界与限制
- 字段级权限与 Collection 级权限是两层:Collection 的
read返回false时,用户连文档都读不到,字段级规则无从触发;先过集合级,再看字段级。 - 对由 slug 引用的 blocks(
blocks: ['someSlug']),其访问控制只执行一次,block 的访问控制中拿不到集合数据(blocks 文档说明)。 - 默认认证字段(如
apiKey)同样可以加访问控制:自定义字段配置会与生成的默认字段合并,默认 hooks 与 Admin Panel 配置保留(default-fields 文档)。 - Admin Panel 会动态响应访问控制变化:某集合对当前用户所有操作都被拒绝时,该集合在 Admin Panel 中直接隐藏。
需要更完整的角色划分示例时,可以参考仓库内的 examples/auth 目录(Admin Panel 文档指向的 RBAC 完整示例),并继续查阅 Collection Access Control 与 Globals Access Control 补齐集合和全局层面的规则。
【免费下载链接】payloadPayload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.项目地址: https://gitcode.com/GitHub_Trending/pa/payload
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考