简体中文
  • saas development
  • multi-tenant saas
  • saas architecture
  • saas boilerplate

构建多租户 SaaS 应用程序:从设计到实现的完整指南

学习如何仅在几个小时内高效地构建具有强大身份认证、组织管理和基于角色的访问控制的多租户 SaaS 应用程序。

Yijun
Yijun
Developer

像 Notion、Slack 或 Figma 这样的应用是如何构建的?这些多租户 SaaS 应用看起来使用简单,但是自己构建一个?那是另外一个故事。

当我第一次想到构建这样一个复杂的系统时,我的脑袋爆炸了:

  • 用户需要多种登录选项(电子邮件、谷歌、GitHub)
  • 每个用户可以创建和属于多个组织
  • 每个组织中不同的权限级别
  • 企业组织需要对特定电子邮件域的自动加入功能
  • 对敏感操作的 MFA 要求
  • ...

"老板,让我们两周后讨论产品设计。我现在遇到困难了。"

但是当我真正开始工作的时候,我发现事情并不像看起来那么令人望而生畏。

我用不到两个小时的时间构建了一个拥有所有这些功能的系统!

documind-home-page.png

Documind 仪表板Documind 组织页面

我将向你展示如何从头到脚设计和实现这样一个系统——你会惊讶于在 2025 年有现代工具和正确的架构方法,它实际上是多么简单。

本文末尾提供完整的源代码。让我们开始吧!

我们将从一个名为 DocuMind 的 AI 文档 SaaS 产品开始。

DocuMind 是一个 AI 文档 SaaS 产品,设计采用多租户模型,支持个人用户、小企业和企业用户。

该平台为组织内的文档管理提供强大的 AI 能力,包括自动摘要生成、关键点提取和智能内容推荐。

SaaS 身份验证和授权需要哪些功能?

首先,让我们回顾一下必要要求。你需要哪些功能?

多租户架构

为了实现多租户架构,你需要一个称为 组织 的实体层。这种设计模拟一个用户池中的用户可以访问多个工作空间。每个组织代表一个工作空间,而用户保持单一身份,与各工作空间组织的角色链接。

multi-tenant-app-architecture.svg

这是身份验证提供者中广泛使用的功能。身份管理系统中的组织对应于你的 SaaS 应用的工作空间、项目或租户。

organization-examples.png

成员资格

成员是一个临时的概念,用于指示身份在组织中的成员资格状态。

例如,Sarah 使用她的电子邮件 [email protected] 注册你的应用程序。她可以属于不同的工作空间。如果 Sarah 是 工作空间 A 的成员但不是 工作空间 B,那么她被认为是 工作空间 A 的成员而不是 工作空间 B

角色和权限设计

在多租户架构中,用户需要具有特定 权限角色 以访问其租户资源。 权限是定义特定操作的详细访问控制,例如 read: orderwrite: order。它们决定了对特定资源可以执行的操作。

角色是在多租户环境中分配给成员的一组权限。

你需要定义这些角色和权限,然后将角色分配给用户,有时它可能包括自动化过程。例如:

  1. 加入组织的用户自动获得 成员 角色。
  2. 第一个创建工作空间的用户被自动分配为 管理员 角色。

注册和登录流程

确保用户友好并安全的注册和身份验证过程,包括基本的登录和注册选项:

  1. 电子邮件和密码登录:传统的登录方法使用电子邮件和密码。 2. 无密码登录:使用电子邮件验证码进行简单安全的访问。 3. 帐户管理:用户可以在帐户中心更新他们的电子邮件、密码和其他详细信息。 4. 社交登录:诸如谷歌和 GitHub 之类的快速登录选项。 5. 多因素身份验证 (MFA):通过允许使用如 Duo 之类的认证器应用程序登录来增强安全性。

租户创建和邀请

在多租户 SaaS 应用程序中,用户流程的一个关键区别是在租户创建和成员邀请的支持。这一过程需要在产品激活和增长方面进行精心规划和执行。

以下是几种典型的使用流,你需要考虑它们:

用户类型入口点
新帐户从登录和注册页面进入以创建新租户
现有帐户在产品中创建另一个租户
现有帐户收到新的租户邀请从登录和注册页面进入
现有帐户收到新的租户邀请从邀请电子邮件进入
新帐户收到新的租户邀请从登录和注册页面进入
新帐户收到新的租户邀请从邀请电子邮件进入

以下是几乎在每个 SaaS 应用中都可以找到的常见场景。将这些作为参考,以启发你的产品和设计团队,当然你也可以根据需要创建自定义的使用流程。

新帐户创建租户现有用户创建另一个租户
现有用户登录现有用户通过电子邮件加入
新用户登录新用户通过电子邮件加入

技术架构和系统设计

一旦我们理解了所有产品需求,让我们继续实现。### 定义认证策略

身份验证看起来很吓人。用户需要:

  • 电子邮件 & 密码注册/登录
  • 一键登录 Google/GitHub
  • 找回时重置密码
  • 为企业客户提供团队范围内的登录
  • ...

仅实现这些基本功能可能需要数周的开发时间。

但是现在,我们不需要自己构建任何这些功能!

现代身份验证提供者(这次我将选择 Logto)已经为我们打包了所有这些功能。身份验证流程十分简单:

从数周的开发时间到 15 分钟的设置时间,Logto 为我们处理所有复杂的流程!我们将在后面的实现部分介绍集成步骤。现在我们可以专注于构建 DocuMind 核心功能!

建立多租户架构

组织系统允许用户创建和加入多个组织。让我们理解核心关系:

在这个系统中,每个用户可以属于多个组织,而每个组织可以有多个成员。

在多租户应用程序中启用访问控制

基于角色的访问控制 (RBAC) 对于确保多租户 SaaS 应用程序的安全性和可扩展性非常重要。

在多租户应用程序中,权限和角色的设计通常保持一致,因为它们源于产品设计。例如,在多个工作空间中,通常有一个管理员角色和一个成员角色。作为身份验证服务提供者的 Logto 具有以下组织级别的基于角色的访问控制设计:

  1. 统一的权限定义:权限在系统级定义,并在所有组织中一致地应用,确保可维护且一致的权限管理
  2. 组织模板:通过组织模板预定义角色和权限组合,简化组织初始化

权限关系如下所示:

由于每个用户在每个组织中需要自己的角色,因此角色和组织之间的关系必须反映分配给每个用户的角色:

我们已经设计了组织体系和访问控制系统,现在我们可以开始建立我们的产品!

技术栈

我选择了一个适合初学者的便携式栈:

  1. 前端:React (可以很容易地转移到 Vue/Angular/Svelte)
  2. 后端:Express (简单,直观的 API)

为什么要分离前端和后端?因为它具有清晰的架构,易于上手和简单更换栈。对于身份认证服务提供者,我以 Logto 为例。

对于以下指南,这里的模式适用于:任何前端,任何后端和任何身份认证系统。

向你的应用程序添加基本身份验证流程

这是最简单的步骤。我们只需要将 Logto 集成到我们的项目中。然后我们可以根据需要在 Logto 控制台中配置用户登录/注册方法。

安装 Logto 到你的应用程序

首先,登录到 Logto Cloud。如果你没有帐户,可以注册一个免费帐户。创建一个开发租户进行测试。

在租户控制台中,点击左边的“Application”按钮。然后选择 React 开始构建我们的应用程序。

按照页面上的指南进行操作。你可以在大约 5 分钟内完成 Logto 集成!

这是我的集成代码:

documind-home-page.png

这是一个有用的技巧:我们的登录页面有登录和注册按钮。注册按钮直接引导至 Logto 的注册页面。这通过 Logto 的 first screen 功能实现。它确定用户首次看到身份验证流程的哪个步骤。

在你的产品预期有很多新用户的情况下,可以默认进入注册页面。

点击登录后,你将进入 Logto 的登录页面。成功登录(或注册)后,恭喜!你的应用程序有了第一个用户(你自己)!

在你需要时,从 useLogto 钩子中调用 signOut 函数注销用户。

定制登录和注册方法

在 Logto 控制台中,点击左侧菜单中的“登录体验”。然后点击“注册和登录”标签。 在此页面,按照说明配置 Logto 的登录/注册方法。

sign-in-experience-settings.png

登录流程将如下所示:

Logto 登录页面

启用多因素身份验证

在 Logto 中,启用 MFA 很简单。只需在 Logto 控制台中点击“多因素身份验证”按钮。然后在多因素身份验证页面启用它。

mfa-settings.png

MFA 流程将如下所示:

Mfa 验证步骤在身份验证器应用中扫描二维码

这一切都很简单!我们在短短几分钟内设置了一个复杂的用户身份验证系统!

添加多租户组织体验

现在我们有了第一个用户!然而,这个用户还没有属于任何组织,我们也还没有创建任何组织。

Logto 内置支持多租户。你可以在 Logto 中创建任意数量的组织。每个组织可以有多个成员。

每个用户可以从 Logto 获取他们的组织信息。这为多租户支持提供了可能。

获取用户的组织信息

为从 Logto 获取用户的组织信息,请履行以下两个步骤:

在 Logto 配置中声明对组织信息的访问权限。这通过设置 .scopes.resources 完成。

使用 Logto 的 fetchUserInfo 方法来获取用户信息,包括组织数据。

完成这些步骤之后,你需要登出并重新登录。这是由于我们修改了请求的 scope 和 resource。

目前,你还没有创建任何组织。用户也没有加入任何组织。仪表板将显示 "你暂无任何组织"。

dashboard-no-orgs.png

接下来,我们将为用户创建一个组织并将他们添加进去。

感谢 Logto,我们不需要建立复杂的组织关系。我们只需要在 Logto 上创建一个组织并将用户添加到其中。Logto 为我们处理所有的复杂性。有两种方法可以创建组织:

  1. 通过 Logto 控制台手动创建组织
  2. 使用 Logto 管理 API 来创建组织,特别是在设计让用户自助创建自己组织(工作空间)的 SaaS 流程时。

在 Logto 控制台中创建组织

单击 Logto 控制台左侧的“组织”菜单按钮。创建一个组织。

现在你有了第一个组织。

console-organizations.png

接下来,让我们将用户添加到该组织。

进入组织详细信息页面。切换到成员选项卡。点击“+ 添加成员”按钮。从左侧列表中选择你的登录用户。点击右下角的“添加成员”按钮。现在你已成功地将用户添加到该组织。

console-add-member-to-orgs.png

刷新你的 APP 页。你将会看到用户现在属于一个组织了!

dashboard-has-orgs.png

实现自助服务的组织创建体验

在控制台中创建一个组织是不够的。你的 SaaS 应用程序需要一个使终端用户能够轻松创建和管理自己的工作空间的流程。要实现此功能,使用 Logto 管理 API。

有关指导,请查看 Interact with Management API 文档,以设置与 Logto 的 API 通信。

理解组织认证交互流程

让我们以组织创建流程为例。以下是组织创建过程的工作原理:

这一流程对于身份验证有两个关键要求:

  1. 保护后端服务 API:
    • 前端访问我们的后端服务 API 需要经过身份验证
    • 通过验证用户的 Logto 访问令牌来保护 API 端点
    • 确保只有经过身份验证的用户才能访问我们的服务
  2. 访问 Logto 管理 API:
    • 后端服务需要安全调用 Logto 管理 API
    • 请按照 Interact with Management API指南进行设置
    • 使用机器对机器身份验证获取访问凭据

保护你的后端 API

首先,让我们在我们的后端服务中创建一个 API 端点以用于创建组织。

我们的后端服务 API 仅允许经过身份验证的用户。我们需要使用 Logto 来保护我们的 API。我们还需要知道当前用户的信息(如用户 ID)。

在 Logto 的概念(以及 OAuth 2.0 中),我们的后端服务充当资源服务器。用户使用来自前端的访问令牌访问 DocuMind 资源服务器。资源服务器验证此令牌。如果合法,它返回请求的资源。

让我们创建一个 API 资源来表示我们的后端服务。

进入 Logto 控制台。

  1. 点击右侧的“API 资源”按钮。
  2. 点击“创建 API 资源”。在弹出窗口中选择 Express。
  3. 填写“DocuMind API”作为 API 名称。使用“https://api.documind.com”作为 API 标识符。
  4. 点击创建。

不用担心这个 API 标识符 URL。在 Logto 中,它只是你的 API 的唯一标识符。与实际的后端服务 URL 无关。

你将看到有关使用 API 资源的教程。你可以按照那个教程或以下步骤进行。

让我们创建一个 requireAuth 中间件来保护我们的 POST /organizations 端点。

要使用这个中间件,我们需要这些环境变量:

  • LOGTO_JWKS_URL
  • LOGTO_ISSUER

从你的 Logto 租户的 OpenID 配置端点获取这些变量。访问 https://<your-tenant-id>.logto.app/oidc/.well-known/openid-configuration。在返回的 JSON 中你会找到所需信息:

现使用 requireAuth 中间件在我们的 POST /organizations 端点。

这保护了我们的 POST /organizations 端点。只有拥有合法 Logto 访问令牌的用户才能访问它。

我们现在可以从前端的 Logto 获取令牌。用户可以通过此令牌访问我们的后端服务 API 来创建组织。中间件还为我们提供了用户 ID。这有助于将用户添加到组织中。

在前端代码中,在 Logto 配置中声明此 API 资源。将其标识符添加到 resources 数组中。

如前所述,用户在我们更新 Logto 配置后需要重新登录。

在仪表板中,在创建组织时获取 Logto 访问令牌。使用此令牌去访问我们的后端服务 API。

现在我们可以正常访问 DocuMind 后端服务 API。

调用 Logto 管理 API

让我们使用 Logto 管理 API 实现组织创建。

和前端请求后端服务一样,后端服务请求 Logto 也需要访问令牌。

在 Logto 中,我们使用机器对机器认证来获取访问令牌。见 Interact with Management API

进入 Logto 控制台的应用程序页面。创建一个机器对机器应用程序。分配“Logto 管理 API 访问”角色。复制令牌端点,应用程序 ID 和应用程序机密。我们将使用这些进行访问令牌。

m2m-application.png

现在我们可以通过此 M2M 应用程序获取 Logto 管理 API 访问令牌。

使用此访问令牌去调用 Logto 管理 API。

我们将使用以下管理 API:

我们现在已经通过 Logto 管理 API 实现了组织创建功能。我们还可以将用户添加到组织中。

让我们在仪表板中测试此功能。

dashboard-create-org.png

点击“创建组织”

dashboard-has-orgs.png

创建成功!

下一步是邀请用户加入组织。我们暂时不会在我们的教程中实现这个功能。你已经知道如何使用管理 API。你可以参考租户创建和邀请作为产品设计参考,并按照这篇博文轻松实现这个功能:如何在多租户应用中实现用户协作

在你的多租户应用中实现访问控制

现在让我们继续组织访问控制。

我们想实现:

  • 用户只能访问属于他们自己的组织的资源:通过 Logto 的 organization token 实现
  • 用户在组织内具有特定角色(包含不同权限)以执行授权操作:通过 Logto 的组织模板功能实现

让我们看看如何实现这些功能。

使用 Logto 组织令牌

类似于我们之前提到的 Logto 访问令牌,Logto 为特定资源颁发访问令牌,用户使用此令牌访问后端服务中的受保护资源。对应地,Logto 为特定组织颁发组织令牌,用户使用此令牌访问后端服务中的受保护组织资源。

在前端应用中,我们可以使用 Logto 的 getOrganizationToken 方法来获取访问特定组织的令牌。

这里,organizationId 是用户所属的组织的 id。

在使用 getOrganizationToken 或任何组织功能之前,我们需要确保 Logto 配置中包含 urn:logto:scope:organizations 范围和 urn:logto:resource:organization 资源。因为我们已经在之前声明过了,就不再重复了。

在我们的组织页面中,我们使用组织令牌来获取该组织内的文档。

在这个实现中有两个重要的点需要注意:

  1. 如果传递给 getOrganizationTokenorganizationId 不是当前用户所属的组织 id,那么此方法无法获取到令牌,从而确保用户只能访问他们自己的组织。
  2. 请求组织资源时,我们使用组织令牌而不是访问令牌,因为对于组织所属的资源,我们希望使用组织权限控制而不是用户权限控制(当我们稍后实现 GET /documents API 时,你将更好地理解这一点)。

接下来,我们在后端服务中创建 GET /documents API 。类似于我们之前稍提到的 API 资源如何保护 POST /organizations API ,我们使用组织特定的资源指示符来保护 GET /documents API 。

首先,让我们创建一个 requireOrganizationAccess 中间件来保护组织资源。

然后我们使用 requireOrganizationAccess 中间件保护 GET /documents API 。

这样我们就实现了使用组织令牌访问组织资源。在后端服务中,你可以根据所提供的组织 id 从数据库检索相应的资源。

一些软件要求每个组织之间的数据隔离。有关进一步的讨论和实现,你可以参考博文:使用 PostgreSQL 实现多租户:通过一个简单的现实示例学习

实现组织级别的基于角色的访问控制设计

我们实现了使用组织令牌访问组织资源。接下来,我们将通过 RBAC 在组织内部实现用户权限控制。

假设 DocuMind 有两个角色:管理员和合作者。

管理员可以创建和访问文档,而合作者只能访问文档。

因此,我们的组织需要有这两个角色:管理员和合作者。

管理员拥有 read:documentscreate:documents 权限,而合作者仅拥有 read:documents 权限。

  • 管理员
    • read:documents
    • create:documents
  • 合作者
    • read:documents

这就是 Logto 的组织模板功能的作用。

一个组织模板是每个组织访问控制模型的蓝图:它定义了适用于所有组织的角色和权限。

为什么需要组织模板?

因为 SaaS 产品最重要的需求之一就是可扩展性。换句话说,适用于一个客户的方案应该适用于所有客户。

让我们转至 Logto 控制台 > 组织模板 > 组织权限并创建两个权限:read:documentscreate:documents

org-template-permission.png

然后转至组织角色选项卡以创建两个用户角色:管理员和合作者,并将相应权限分配给这些角色。

organization-details.png

这样我们就为每个组织创建了 RBAC 权限模型。

接下来,我们转至我们的组织详细信息页面为我们的成员分配适当的角色。

org-template-role.png

现在我们的组织用户有角色了! 你可以通过 Logto 管理 API 实现这些步骤:

现在我们可以通过检查用户的权限来实现用户权限控制。

在我们的代码中,我们需要让用户的组织令牌携带权限信息,然后在后端验证这些权限。

在前端代码的 Logto 配置中,我们需要声明用户在组织内需要请求的权限。让我们将 read:documentscreate:documents 权限添加到 scopes 中。

通常情况下,使用你的用户登录来使这些配置生效。

然后在后端的requireOrganizationAccess 中间件中增加对用户权限的验证。

然后创建 POST /documents API,并使用 requireOrganizationAccess 中间件启用 requiredScopes 配置以保护此 API 及以前的 GET /documents API 。

这样我们就通过检查用户权限实现了用户权限控制。

在前端,你可以通过解码组织令牌或调用 Logto 的 getOrganizationTokenClaims 方法来获取用户权限信息。

通过检查声明中的 scopes 来控制页面元素。

添加更多多租户应用功能

到目前为止,我们已经在多租户 SaaS 系统中实现了基本的用户和组织功能!然而,还有一些功能我们尚未涵盖,比如为每个组织自定义登录页面品牌、自动将特定域的电子邮件用户添加到某些组织中,以及集成企业级 SSO 功能。

这些都是开箱即用的功能,你可以在 Logto 文档中找到有关这些功能的更多信息。

总结

还记得一开始那种压倒性的感觉吗?用户、组织、权限、企业功能……看起来似乎像是一座无尽的山峰等着去翻越。

但看看我们所取得的成就:

  • 一个完整的身份验证系统,提供多种登录选项和 MFA 支持
  • 一个支持多成员灵活的组织系统
  • 组织中基于角色的访问控制

最棒的是?我们不必从头开始。通过利用 Logto 等现代工具,我们把本可能是几个月的开发工作缩短到了几个小时。

本文的完整源代码可在以下链接找到:Multi-tenant SaaS Sample

这就是 2025 年现代开发的力量——我们可以专注于构建独特的产品特性,而不是和基础设施纠缠不清。现在轮到你来创造一些伟大的东西了!

Logto 网站 探索所有的 Logto 功能,从 Logto 云到 Logto 开源,或立即注册 Logto 云