指南

如何为应用添加基于角色的访问控制(RBAC)

要添加基于角色的访问控制,需要为每位用户分配一个或多个角色(例如管理员、编辑、查看者),定义每个角色可以执行哪些操作,并且在服务端对每个请求检查这些权限——而不仅仅是隐藏按钮。还要检查被访问的记录是否属于该用户的账户或团队。先在数据库中设置几个固定角色,然后通过尝试直接访问其他用户的数据来进行测试。

身份认证与授权

两个容易混淆的概念:

  • 身份认证回答的是你是谁?——即登录。参见如何为应用添加登录功能。
  • 授权回答的是你被允许做什么?——即已登录的用户可以访问哪些页面、记录和操作。

RBAC 是最常见的授权方式。这一点很重要:截至 2026 年 9 月,失效的访问控制在 OWASP Top 10:2025(广泛使用的最严重 Web 应用安全风险清单)中排名第一。典型的失效情形包括:用户通过修改 URL 中的 ID 读取其他用户的记录,或普通用户调用仅限管理员使用的 API 路由。

选择模型

模型工作方式适用场景权衡
单一角色字段users.role = admin / member只有两三个角色的小型应用当同一个人需要在不同场景下拥有不同角色时,会变得混乱
角色 + 权限角色映射到已命名的权限(invoices.approve);代码检查权限规则会不断增长的应用表更多;需要维护一份权限列表
按团队(多租户)划分的角色成员关系表:用户 + 组织 + 角色人员属于团队或工作区的 SaaS每个查询都必须限定在对应的组织范围内
基于属性或关系的模型基于记录属性的规则(“作者可以编辑自己的帖子”)或基于关系的规则(与…共享)文档、共享及复杂的工作流较难推理;可考虑专门的库或服务
认证服务商的角色你的登录服务商存储角色或组织成员关系已在使用托管认证服务的应用依赖该服务商;仍需在你的服务端强制执行

实际上,大多数应用会结合两件事:角色决定某人可以执行哪类操作,归属关系决定他们可以对哪些记录执行这些操作。“编辑可以编辑帖子”是角色规则;“……且限于其自己工作区中的帖子”则是归属规则。两者你都需要。

从比你想象中更简单的方案开始。管理员、成员和查看者这三个角色,就足以满足相当多应用的需求。之后拆分一个角色,比理清十个没人能解释清楚的角色要容易得多。

编写代码之前先设计角色

先用通俗的语言写一张权限表。它既可以作为给 AI 的规格说明,也可以作为你的测试计划:

操作所有者管理员成员查看者
查看团队中的项目✓✓✓✓
创建和编辑项目✓✓✓
删除项目✓✓
邀请和移除成员✓✓
更改角色✓
账单✓

也要决定边界情况:最后一位所有者能否离开?管理员能否移除所有者?被移除的用户会看到什么?

分步操作

1. 先确保登录功能正常

RBAC 构建在身份认证之上。服务端必须能够可靠地知道每个请求是谁发出的,依据是会话或经过验证的令牌——绝不能依据浏览器在请求体中发送的用户 ID。

2. 将角色存储在数据库中

对于只有单一团队的应用,用户表中的 role 字段就足够了。对于多团队的情况,使用成员关系表:

create table memberships (
  user_id uuid references users(id),
  organization_id uuid references organizations(id),
  role text not null check (role in ('owner','admin','member','viewer')),
  primary key (user_id, organization_id)
);

绝不要让用户通过普通的个人资料更新来设置自己的角色。角色变更应通过专门的、经过权限检查的端点进行。

3. 在服务端集中检查权限

编写一个统一的辅助函数,例如 can(user, 'projects.delete', project),并在每个读取或修改数据的 API 路由、服务端操作和后台任务中调用它。集中的一个函数比分散在代码各处的检查更容易审查和测试。默认拒绝:如果没有任何规则允许某项操作,则拒绝执行。

4. 限定每个查询的范围

每个查询团队数据的语句都应包含团队信息:使用 where organization_id = :currentOrg,而不只是 where id = :id。这样才能防止有人把 /projects/123 改成 /projects/124 就看到另一家公司的项目。对于其他团队的记录,应返回“未找到”而不是“禁止访问”,以免暴露这些记录的存在。

5. 在关键位置添加数据库级保护

Postgres 行级安全(RLS)允许数据库强制规定每位用户可以看到哪些行。如果浏览器直接与数据库通信(例如某些托管后端),这一点至关重要;在其他情况下,它也是敏感表的有用第二层防护。

6. 在界面中反映角色

现在隐藏或禁用用户无权执行的操作,使应用不会让人困惑。这只是为了易用性;真正起保护作用的是服务端的检查。

7. 记录敏感操作

记录谁在何时更改了角色、删除了数据或导出了记录。审计日志有助于处理支持请求和安全事件。

8. 像攻击者一样测试

以每个角色登录,并尝试权限表中的每一项操作。然后尝试那些本不应成功的操作:以查看者身份调用管理员 API 路由,在 URL 和请求中修改 ID,以及把自己作为最后一位所有者移除。规则确定之后,值得为权限表编写自动化测试。

行之有效的提示词

添加基于角色的访问控制。每个组织的角色:所有者、管理员、成员、查看者,存储在成员关系表中。权限定义见下表:[粘贴你的表格]。创建一个服务端的 can(user, action, resource) 辅助函数,默认拒绝,并在每个 API 路由中使用它。所有查询都按 organization_id 限定范围。在界面中隐藏用户无法执行的操作,但绝不能依赖这一点来保证安全。

审查本应用中的每一个 API 路由和服务端函数。对于每一个,列出它读取或修改了哪些数据,是否检查用户已登录,是否检查其角色,以及是否将查询限定在其所属组织内。修复所有缺少检查的地方,并向我展示该清单。

添加一个仅限管理员访问的 /admin 页面,让所有者可以更改成员的角色。禁止移除或降级最后一位所有者,记录每次角色变更的执行人和时间,并确保角色变更端点会拒绝任何非所有者的请求。

常见错误

  • **只在前端检查角色。**API 仍然对任何调用它的人开放。
  • 信任客户端传来的 ID——例如从请求体而不是会话中获取 userId 或 role。
  • 未限定范围的查询,仅凭记录 ID 进行查询。
  • 只在页面上检查角色,而没有在 API 路由、导出、文件下载或 Webhook 中检查。
  • 在代码中硬编码通过邮箱地址识别管理员(if email === 'me@...'),这很容易被遗忘,也很难修改。
  • 过早设置过多角色,导致六个月后没人能解释清楚。
  • 会话过期不及时:被降级的用户在退出登录之前仍保有管理员权限。对于敏感操作,应从数据库检查角色,或刷新角色信息。

RBAC 检查清单

  • 已用通俗语言写出权限表,并已确定边界情况
  • 服务端从会话中识别用户,绝不从请求数据中获取
  • 角色存储在数据库中;用户无法更改自己的角色
  • 一个集中的权限检查,默认拒绝,并在每条路由中使用
  • 每个查询都限定在用户的组织或归属范围内
  • 浏览器能够访问数据库的地方已启用行级安全
  • 界面隐藏了用户无法执行的操作(仅为易用性)
  • 敏感操作已记录日志
  • 已测试每个角色,包括直接调用 API 和修改 ID 的情况

访问控制只是更长清单中的一项;AI 构建应用的安全检查清单涵盖了其余内容。如果你要为自己的员工构建工具,如何搭建内部工具展示了角色在其中的位置。

Mythex 中的 RBAC

Mythex 不为你应用的用户提供内置的登录或角色系统。你需要选择认证库或服务商,并描述角色,代理会将它们接入你的代码和项目的 Postgres 数据库(为应用添加登录功能)。Mythex 自身的工作区角色(例如所有者和成员)控制的是谁可以编辑你的 Mythex 项目,而不是谁可以使用你发布的应用。在构建之前,使用计划模式先确认权限表;然后使用上面的审查提示词,并在发布之前于预览中测试每个角色。文档中的安全提示词是很有用的后续参考。

常见问题

什么是基于角色的访问控制?

基于角色的访问控制(RBAC)根据每位用户所持有的角色(例如管理员、编辑或查看者)来决定其可以做什么。权限附加在角色上,而不是附加在具体的个人身上,因此改变某人的权限只需改变其角色。

身份认证和授权有什么区别?

身份认证用于确认某人是谁,通常通过登录完成。授权则决定已登录的人可以查看和执行哪些操作。RBAC 是实现授权的一种方式。

在界面中隐藏按钮就足以限制访问了吗?

不够。任何人都可以直接调用你的 API,因此隐藏按钮只会改变人们看到的内容。每一个读取或修改数据的请求,都必须在服务端根据用户的角色以及记录的归属进行检查。

我应该使用 Postgres 行级安全吗?

行级安全允许数据库本身强制规定每位用户可以读取或写入哪些行,是一道很有力的安全保障,尤其是在前端直接与数据库通信的情况下。但它会增加复杂性,因此许多应用在服务端代码中强制执行权限,并对敏感表额外使用行级安全作为补充防护层。

Mythex · Pricing · 用 AI 构建任何东西 · 网站和应用模板 · Mythex 对比 · AI 应用构建术语表 · 免费的 AI 应用构建工具 · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 繁體中文(香港) 繁體中文(台灣)