[ PROMPT_NODE_25330 ]
saas-multi-tenant
[ SKILL_DOCUMENTATION ]
# SaaS 多租户架构
## 何时使用此技能
- 用户正在构建一个多个客户共享同一数据库的 SaaS 应用程序
- 用户询问租户隔离、行级安全性 (RLS) 或数据泄露预防
- 用户需要将每个数据库查询限定在特定租户范围内,而无需手动编写 WHERE 子句
- 用户询问共享模式 (shared-schema) 与每租户模式 (schema-per-tenant) 与每租户数据库 (database-per-tenant) 的权衡
- 用户正在实现必须跨租户访问数据的管理端点
- 用户需要向现有的单租户应用程序添加 `tenant_id` 列
- 用户询问用于租户隔离的 PostgreSQL RLS 策略
- 用户正在 Express、Fastify 或 Next.js API 路由中构建租户感知中间件
不要使用此技能的情况:
- 用户正在构建没有共享基础设施的单用户应用程序
- 用户仅询问身份验证而无需租户作用域(请使用身份验证技能)
- 用户需要通用的数据库模式设计,且没有多租户需求
## 核心工作流
1. 确定租户模型。询问用户的规模预期和隔离要求。对于大多数 1000 个租户以下的 SaaS 应用,默认选择在每个表上添加 `tenant_id` 列的共享模式是正确的。每租户模式增加了操作开销(迁移需要运行 N 次)。仅当租户有监管数据驻留要求时,才考虑每租户数据库模式。
2. 为每个租户作用域表添加 `tenant_id`。该列必须是 `NOT NULL`,类型为 `UUID` 或 `TEXT`,并包含在每个复合索引中。严禁在没有此列的情况下存在租户作用域表 — 缺失 `tenant_id` 是导致数据泄露的隐患。
3. 设置 PostgreSQL 行级安全性 (RLS)。在每个租户作用域表上创建策略,通过 `current_setting('app.current_tenant_id')` 过滤行。这充当了数据库级的安全网 — 即使应用程序代码忘记了 WHERE 子句,RLS 也会阻止跨租户读取。
4. 构建租户感知中间件。在每个请求开始时,从经过身份验证的会话或 JWT 声明中提取 `tenant_id`。在事务内部使用 `SET LOCAL app.current_tenant_id = '...'` 将其设置在数据库连接上。该请求中的每个后续查询都会自动继承租户作用域。
5. 使用租户限定所有 ORM 查询。如果使用 Prisma,应用一个全局中间件,将 `where: { tenantId }` 注入到每个 `findMany`、`findFirst`、`update` 和 `delete` 调用中。如果使用 Drizzle,...