概述
在此阶段,我们对控制平面的关注点完全在于构建一个基础架构,以便在架构中引入多租户功能。租户接入、用户管理和租户管理使我们能够配置、捕获并准备租户以进入SaaS环境。现在,是时候开始考虑租户如何利用这些构造(以及其他构造)进入我们多租户环境的入口了。
正是此时,当您对用户进行身份验证时,整个用户注册流程与租户管理模块将完美融合。您将看到存储在租户管理中的配置信息如何在身份验证流程的流转与实现中发挥关键作用。我们还将看到,将用户与租户关联的工作如何为下游服务提供必要的租户上下文,这些服务是您多租户架构的一部分。
在本章中,我将首先探讨如何暴露多租户解决方案的入口点。访问SaaS环境有多种策略,其中一些明确标识进入系统的租户,而另一些则依赖内部机制来确定访问系统的租户。这些策略对租户的身份验证方式以及与适当身份提供商的连接方式都有影响。
我们还将探讨通过前门路径进入系统的方式如何影响多租户环境的身份验证模型。在此过程中,我还将分析如何利用不同的身份提供商构造来支持多种租户身份验证体验。您将看到我们在第4章讨论的身份策略如何在开始验证单个租户时发挥作用。
本章的最后部分将探讨此身份验证体验如何映射到多租户架构的下游组件。这包括探讨JWT如何注入应用程序的服务,以及如何利用身份验证上下文将租户请求路由到多租户环境中的特定组件。这种路由上下文通常与访问SaaS应用程序的方式紧密相关。
基本目标是将您的SaaS环境提升到一个新的水平,基于第1章至第5章建立的基础。在这里,我们看到实际租户如何进入SaaS环境,以及我们建立的构造如何使我们能够认证租户并获取所需的上下文,以塑造您SaaS架构中下游多租户环境的其余部分。
进入前门
现在我们对身份验证的范围有了初步了解,让我们从最自然的起点开始探索——前门。身份验证的核心要素始终始于确定租户如何访问您的应用程序。尽管这些访问模式看似简单,但您将逐渐发现,在此处选择的策略远不止于用于向租户暴露应用程序的URL。
在考虑如何让租户访问您的系统时,您有多种选择。例如,您可以让系统的域名包含租户名称,并依赖此域名作为租户映射和路由策略的一部分。或者,您可以允许租户使用基于品牌或其他考虑因素的独立域名。无论采用哪种方式,域名通常仍会在识别访问系统租户的过程中发挥一定作用。然而,部分SaaS解决方案并不依赖域名,而是为所有租户使用单一域名。在此方案中,您需要通过环境的身份验证流程将租户上下文注入系统。
关键在于——即使在选择进入应用程序的路径时——您所做的选择可能会对SaaS架构的其他维度产生连锁影响。这种访问策略甚至可能影响用于实现解决方案特定组件的技术和服务。
在接下来的章节中,我们将探讨在为SaaS环境选择访问模型时常用的几种模式。对于每种模式,我们将回顾与之相关的考虑因素。
通过租户域访问
租户进入应用程序前门的常见方式之一是通过域。更具体地说,租户可以使用包含用于识别租户信息的域进入系统。图6-1展示了该域的上下文如何影响多租户架构的下游足迹。
图6-1
图6-1展示了多个租户通过域访问SaaS环境的场景。在此模型中,每个租户的域在注册过程中进行配置。配置完成后,域URL将作为入口点分享给租户,用于访问您的SaaS服务。
采用这种方法,来自这些不同域的所有入站租户请求都会通过我标记为“租户映射”的组件进行路由。这个框代表了一个概念上的占位符,用于表示用于将租户域映射到相应后端服务的各种技术、基础设施和服务。该映射服务通常用于从域中提取入站租户上下文,并使用该上下文解析映射到专用租户资源的映射关系。
映射至少有两个常见的应用场景。第一个是身份验证。当系统验证租户用户时,可能需要将传入的身份验证请求映射到对应的身份构造。这主要适用于没有单一共享身份提供商的租户环境。在这些情况下,可能需要独立的身份提供者来支持身份验证,或者在单个身份提供者内存在与特定租户绑定的独立身份构造(如组、用户池等),以提供特定功能或体验。
在图6-1中,您可以直观地看到身份验证映射的实现过程。在此示例中,租户通过其专属域名访问系统,该域名随后经过租户映射服务,从请求源中提取租户信息。这些信息用于在租户管理中查找租户身份设置,进而将租户用户与目标身份提供商或构造进行身份验证。
租户映射的另一个应用领域与应用程序请求的路由相关。当您的架构使用一个或多个孤立的租户资源时,传入的租户请求需要被路由到这些特定资源。在此场景中,映射服务必须使用租户上下文来确定给定请求应采取的路由。我们将在本章后面的内容中详细讨论此用例。
虽然支持独特租户域的技术可能有所不同,但其核心原理通常大同小异。具体如何、何时以及在何处应用这些技术,将取决于您在该域上下文中需要实现的功能。它可能用于查找租户标识符、将租户用户映射到身份提供商、配置路由,或执行任何其他需要租户上下文来处理请求的操作。
值得注意的是,域名(或子域名)代表一个友好、公开可见的名称,您将其作为租户的URL入口暴露出来。该名称在租户生命周期中可能发生变化,因此与租户标识符的内部概念完全分离,后者在租户生命周期中保持不变。
基于租户的子域名模型
此时,一个租户一个子域的模型(domain-per-tenantmodel)的基本原理应已清晰。当然,尽管我之前以通用方式讨论了域,但实际中存在多种将域与租户关联的方式。在许多SaaS环境中,人们希望使用域来标识租户,而无需每个租户都拥有完全唯一的域。在此场景下,假设有一家SaaS公司(例如ABCSoftware)拥有abc-software.com域,并希望将该域作为其SaaS体验的呈现和品牌标识的一部分。在此示例中,其客户也不认为拥有独立域具有价值。
在这种情况下,使用子域名作为识别租户的方式非常自然,无需为每个租户创建全新的域名。以abc-software.com为例,您可以在现有域名前面添加一个友好的租户名称。最终结果将生成类似tenant1.abc-software.com和tenant2.abc-software.com的域名。您可能已经在当前使用的现有SaaS解决方案中见过这种模式的实现。
这种方法对SaaS提供商非常有吸引力,因为它允许为每个租户提供唯一的访问点,同时避免了为每个租户创建唯一域名的复杂性和开销。
租户专用域名模型
另一种方案是为每个租户使用专属域名。这种方法常用于租户需要向客户展示品牌化体验的场景。事实上,在这些场景中,租户的用户可能完全不知道自己正在运行的底层SaaS系统。
为更清晰理解该模型,以某SaaS供应商提供的电商解决方案为例:租户通过该SaaS环境配置并托管自有品牌商店。在此场景下,租户将拥有专属域名用于访问您的多租户环境。
从多数方面看,此品牌域名模型与前文所述子域名模型极为相似。它依然通过源域名提取租户名称,再将其映射至租户上下文供下游使用。值得注意的是,该模型也可作为白标策略的一部分,允许租户应用自有品牌。例如,设想一个电商SaaS平台,平台上的每家店铺都使用个性化域名,并将自有品牌贯穿整个用户体验。
租户域名的接入流程
当您使用域名来标识租户时,必须考虑这将如何影响租户入驻模型的设计。现在,每次创建新租户时,您都需要配置这些域名的设置,并创建任何必要的映射,以支持租户进入您的环境。
基于子域名的租户模型对入驻流程的影响较小。在此模型中,您的主要工作是配置环境中涉及的各种DNS设置选项。图6-2展示了在基于子域名的租户模型中,为新租户入驻配置的部分元素示例。
我已概述了一个具体的AWS示例,展示了您需要如何配置网络路由的各个组件以支持每个租户一个子域模型。在图的顶部,我展示了将处理租户请求的内容分发网络(CDN)的配置。它使用了亚马逊的CloudFront服务。下表说明了如何配置CDN以支持租户子域名。CDN表的第一行包含在初始配置基础SaaS环境时设置的参数。在此示例中,通用域名被分配了别名app.sassco.com,其中“saasco”表示我们虚构的SaaS公司的品牌域名。当新租户加入系统时,您必须向此CDN表添加新行。在此示例中,我已添加一个新租户,其配置为tenant1.sassco.com。
图6-2
除了配置CDN之外,我们还必须为这个新租户子域名设置DNS路由。图6-2底部的表格提供了一个配置DNS服务的示例。在此示例中,我们展示了如何使用AmazonRoute53服务实现此配置。表格中包含两行。第一行包含在初始环境设置时填充的基线值。第二行在接入租户1时填充。这会创建一个A记录,将租户1的子域名(tenant1.saasco.com)指向SaaS环境的通用域名。
虽然实现此功能的许多步骤与自定义域名模型类似,但有一个关键差异需要在域名接入流程中考虑。当租户需要自定义域名时,您的接入流程必须支持在多租户环境中激活该域名的机制。如果域名已存在并需迁移至您的环境,接入流程需包含完成此迁移的必要步骤。然而,也可能存在租户在入驻过程中自行创建域的情况。如您所想,这将是一个更为复杂的过程。复杂性通常集中在验证和注册新域所需的各种步骤上。选择自动化整个体验可能对某些团队而言是一项重大投资。
虽然我使用AWS服务来演示入职体验,但配置此体验的基本原理在其他工具和服务中可能类似。关键在于您需要考虑如何在入职新租户时自动化配置这些域名设置。
通过单一域名访问
到目前为止,我主要讨论了基于域来识别进入应用程序前门的租户的策略。然而,一些SaaS提供商——尤其是采用B2C模式的——会为所有租户使用单一域。在这种情况下,所有租户将使用相同的域访问您的SaaS环境。图6-3展示了一个单域环境的概念视图。
图6-3
在此示例中,我有两个租户通过单一共享域名www.saasco.com访问我的系统。当这些租户尝试验证现有租户用户时,它们会被重定向至我们控制平面中托管的身份提供商。由于该模型采用全局身份构造来存储所有租户用户,因此可通过单一端点对所有租户进行身份验证,且不会遇到实际挑战。当然,将所有用户存储在单一身份提供商构造中也意味着系统中的每个用户只能与一个租户相关联。
当你的架构支持更多种类的身份验证体验时,情况就变得更有趣了。如你所记得的,在第4章中,我提到了身份提供商可能为用户提供不同的分组结构,这使得你可以为每个租户设置独立的身份验证策略。如果你的身份提供商支持这些分组结构,这将使你能够为解决方案的不同层级提供不同的身份验证选项。
图6-4展示了采用独立身份构造的单域策略的更新视图。
图6-4
这与图6-3中的示意图基本一致。唯一的变化是,我们现在为两个租户分别设置了独立的身份构造。这是单域模型中较为复杂的部分。由于缺乏域来识别租户在进入环境时的身份,您无法确定应使用哪个身份构造来认证用户。无论请求由哪个租户发起,每个租户的请求在环境中看起来都相同。
解决身份验证流程中租户上下文的问题有几种方法。一种策略是使用用户电子邮件地址的域来将用户与特定租户关联。在此模型中,假设来自特定域/客户的租户将映射到该客户的租户。如果可以假设来自某个域的所有租户都属于特定租户,这种方法可行。然而,这会限制您支持具有不同电子邮件域的更广泛用户群的能力。另一种可能性是考虑将个别用户标识符映射到特定租户。图6-5提供了此方法的概念视图。
图6-5
在此示例中,我们继承了本节中一直使用的同一单一域模型,使用www.saasco.com作为所有租户的单一入口点。我还添加了两个示例用户标识符,它们代表身份验证过程中使用的不同值。要在该模型中进行身份验证,您需要先将用户标识符解析为对应的租户。这通过与租户管理服务关联的表格实现,该表格将单个用户映射到租户。在实际场景中,此映射很可能是从用户到租户身份构造的映射。一旦获得该身份构造,即可将用户与目标身份构造进行身份验证。此模型仍依赖于用户ID与租户之间的一对一关系。然而,它允许为租户层级配置独立的身份策略。
中间人挑战
在查看图6-5中的解决方案时,您会发现该策略依赖于一定程度的间接映射来成功将租户映射到其身份提供商。这确实会带来一些挑战,尤其是当您试图严格遵循标准身份验证模型时。为了更好地理解这一点,让我们退一步,考虑一个更传统的Web应用程序身份验证实现。
图6-6展示了一个经典的身份验证流程,您可能已经多次实现过。该流程从租户用户尝试访问您的Web应用程序(步骤1)开始,随后被重定向到身份提供商进行身份验证(步骤2)。当用户成功验证后,身份提供商返回包含身份和授权信息的令牌(步骤3)。身份提供商随后将用户以已认证身份返回至网页应用程序(步骤4),并携带此认证上下文调用一个或多个下游微服务(步骤5)。
图6-6
此流程的优势在于,它完全由环境中的身份构造进行协调。从身份提供商到Web应用程序的整个路径大多不受代码控制,并符合身份提供商支持的标准身份验证流程。
理想情况下,您应能够在不偏离此流程太远的情况下实现您的SaaS解决方案。同时,我也讨论过不同的身份配置和访问模式可能依赖于能够将租户映射到其对应身份构造的代码。这些策略通常需要在身份验证体验流程中注入额外的映射构造。
例如,在图6-5中,您看到租户管理服务被用于查找并映射用户到身份构造。这种方法意味着您的身份验证流程无法直接访问身份提供商来处理身份验证。相反,它必须首先确定目标身份构造,然后驱动重定向到适当的身份模型。
每次在身份验证流程中添加此类间接层,您实际上都在环境的身份验证模型中引入了新的扩展点和故障点。虽然这可能是您所需的实现方式,但务必确保已充分权衡在身份验证流程中插入自身组件所带来的利弊。
多租户身份验证流程
到目前为止,您应该清楚地意识到,您进入应用程序前门的方式与多租户架构的整体身份验证体验之间存在密切关联。现在,假设您已有一条路径可将身份验证请求定向到适当的身份提供者构造。假设这些组件已正确配置,我们可以更详细地分析身份验证流程中的剩余步骤。
需要特别注意的是,此过程完全专注于验证租户用户并返回在第4章中描述的SaaS身份。第4章中涵盖的大部分入职流程都是为这一验证时刻做准备,包括配置我们的身份模型,确保包含所有必要组件以验证用户并返回代表租户上下文的令牌,这些令牌将在我们多租户实现的下游组件中使用。
总体而言,此体验的各个组件与大多数身份提供商实现的OAuth和OIDC规范保持一致。然而,了解此体验的端到端流程有助于您更好地理解实际发生的情况。它还通过展示租户上下文在多租户架构讨论的多个维度中的注入过程,连接了关键环节。
一个示例认证流程
虽然我们已经探讨了身份验证概念的各个要素,但让我们通过一个示例流程来查看所有组件在端到端流程中的工作原理(如图6-7所示)。
图6-7
在此示例中,我采用了基于子域名的租户模型作为身份验证流程的一部分。登录到此环境的租户将通过其分配的子域名进入,在本例中为tenant1.saasco.com。租户用户访问我们的Web应用程序(步骤1),应用程序检测到用户未经过身份验证。由于用户未经过身份验证,Web应用程序将用户重定向至应用程序登录界面,用户在此输入凭据。与经典身份验证流程不同的是,我们依赖Web应用程序来检测并引导用户至登录表单。
现在,在验证用户之前,您需要确定与该租户关联的具体身份提供商构造。这通过调用租户管理服务(步骤2)并请求用于验证用户的目标身份构造信息来实现。租户管理服务将检查租户的源(子域),查找其与身份提供商的映射,并将此信息返回给Web应用程序(步骤3)。现在,您已拥有验证租户用户所需的所有信息。此过程的后续步骤遵循OAuth流的经典流程。首先,我们调用身份提供商,并传递租户用户的凭据(步骤4)。身份提供商将返回一个代码(步骤5),随后将此代码交换为JWT(步骤6)。最后,现在您已获得包含租户上下文的JWT,可将此令牌注入对微服务调用的授权头中(步骤7)。
如第4章所述,此流程生成的令牌将作为承载令牌注入下游服务,其中HTTP请求的授权头设置为“承载”令牌,并赋予访问令牌的值。然后,下游服务可以使用此令牌实现对微服务的授权,并为请求提供租户上下文访问权限。
联合身份验证
联合身份验证的复杂性在于,当您的身份足迹可能更加分散时。例如,如果您的解决方案需要对某个外部托管的身份提供商进行身份验证,而该提供商不在您的控制范围内,这将为您的多租户身份验证模型增添一层复杂性。
当您控制身份验证体验的所有方面时,您可以通过身份提供商控制自定义声明和策略。然而,当第三方在联合身份验证模型中验证您的用户时,这些多租户身份验证体验中至关重要的组件如何被支持就变得不明确。您无法要求第三方包含提供租户上下文的自定义声明。同时,我们的多租户设计高度依赖于其能够签发包含此租户上下文的JWT。
好消息是,存在联合身份验证解决方案,能够填补多租户体验中的部分空白。以AmazonCognito为例,您可以在Cognito中为从第三方提供商认证的用户配置自定义声明。在此模型中,当您对联合提供商进行认证时,Cognito可以无缝地将自定义声明整合到从认证返回的JWT中。这使您能够支持第三方提供商,同时仍保留对每个用户自定义声明的管理能力。
在SaaS环境中,您可能需要支持多种不同的联合身份模型。每个身份提供商通常也有自己独特的联合用户的方法。有些可能允许您注入自定义声明,而有些则不允许。关键在于,如果您正在与第三方提供商进行联合身份验证,您必须确定用于获取租户上下文的策略。在某些情况下,这可能需要您操作或注入JWT以实现预期体验。
没有通用的身份验证方案
如您所见,在多租户环境中认证用户存在多种方法。这是SaaS解决方案面临的普遍挑战。应用程序的访问路径、使用的身份构造以及其他因素可能要求您考虑多种不同方法来连接身份流的各个环节。
关键在于,这绝非简单地调用身份提供商并遵循其提供的典型认证流程。相反,您需要将多租户需求与这些身份认证流程相结合,使其与您环境中实现的多租户模式相一致。最终,您确实符合身份提供商支持的OAuth和OIDC规范,但进入这些流程的路径由租户在多租户环境的整体身份架构中的映射方式决定。
认证租户的路由
当您成功进入应用程序的前端并准备调用后端服务时,仍需考虑租户上下文如何影响这些请求的路由。有人可能会认为这不属于身份验证范畴,从技术上讲确实如此。然而,身份验证过程中产生的上下文会直接影响应用程序下游路由的实现。因此,将路由纳入讨论范围显得合乎逻辑。你还会发现,在某些情况下,路由策略可能也会影响你选择的身份验证模型。
在此,当我谈论路由时,通常指的是在请求级别将租户映射到其对应资源的过程。图6-8展示了路由概念模型。
您会发现,有一个租户正在进入我们的SaaS环境的前门,并使用之前讨论过的任何一种身份验证模型进行身份验证。一旦身份验证通过,您的应用程序将开始使用SaaS应用程序的微服务和功能。现在,如果所有应用程序服务都运行在池化模型中,您可以直接调用这些服务。然而,您的应用程序服务架构可能更为多样化,部分服务运行在孤岛模型中,部分运行在池化模型中。
当您拥有混合租户部署模型时,您需要考虑如何将请求路由到相应的租户资源。在图6-8中,我展示了一个简单的概念模型,其中租户1和2使用孤岛资源,其余租户使用池化资源。由于孤岛资源和池化资源的分离,您需要在多租户架构中引入路由概念,以确定如何实现路由。
图6-8
如您所想,您选择的方法将根据所使用的技术栈、应用程序的入口点以及应用程序的部署模型而有所不同。然而,您为用户进入和认证所选择的方法将对您在考虑路由策略时可用的选项产生重大影响。例如,一个租户专属的域名可能与其他网络工具/服务结合,以实现您的路由策略。在考虑应用程序的入口点时,您还应考虑其如何与SaaS环境的路由要求相匹配。
应用程序的路由模型也会影响SaaS解决方案的入驻体验。随着每个新租户入驻系统,您可能需要更新路由基础设施的配置,以验证和配置用于路由该新租户工作负载所需的构造。
不同技术栈下的路由设计
我之前提到过,您所使用的技术栈会对环境中的路由模型产生影响。虽然存在太多可能的组合无法一一涵盖,但我还是想回顾几个不同多租户技术栈的示例,并探讨在这些不同模型中如何实现路由。
为了便于讨论,我将重点分析两种较为常见的SaaS技术模型:无服务器架构和容器化。接下来的内容将探讨这些技术栈在路由设计中的具体细节,帮助您更好地理解在构建多租户感知路由模型时需要考虑的变量。
要深入探讨任何路由策略,我必须讨论具体技术。例如,AWS上无服务器架构可用的工具和机制在Azure或GCP上实现时可能有所不同。然而,Kubernetes的路由模型在不同云平台上可能更为相似(但有例外情况)。
无服务器租户路由
让我们先看看如何实现一种基于无服务器技术的路由策略,以实现其多租户应用程序。在无服务器环境中,您将拥有一组函数,这些函数组合在一起形成代表应用程序功能的各种微服务。
在AWSLambda中,这些函数通常通过API网关进行访问。该网关定义并暴露了服务中的HTTP入口点,将请求映射到对应的函数。从这个角度来看,API网关可视为所有活动流向应用程序服务的核心路径。事实上,当我们在第11章中更详细地探讨无服务器SaaS实现时,您将看到该网关在整体多租户架构中扮演的多种角色。不过,目前我们先专注于网关如何被配置和部署以支持SaaS环境的基本路由需求。
图6-9展示了一个基于AWSLambda构建的基本无服务器SaaS环境的概念视图。在此环境中,我们有一个托管在AmazonS3存储桶中的Web应用程序。该应用程序通过AmazonAPIGateway发送请求,这些请求随后被路由到作为应用程序中各个微服务组成部分的Lambda函数。
如果无服务器应用程序的所有函数都处于池化状态,那么网关的作用就非常简单明了。所有请求都会直接转发至目标函数,无需考虑租户上下文。然而,假设部分或全部微服务(及函数)以孤岛模式运行,此时就需要评估租户上下文并将其请求路由至正确的租户函数。图6-10展示了一个混合部署场景,其中孤岛式和池化计算资源共存,且需要进行精准路由。
图6-9
图6-10
在图6-10中,我采用了按租户部署API网关的模型,为应用的不同层级配置独立的网关。我有两个高级租户(租户1和租户2),其孤岛式Lambda函数实现各自的微服务。此外,基本层租户以池化模式运行,其函数由所有租户共享。若采用混合部署模式(部分隔离、部分池化),则路由将更具上下文依赖性。根据请求类型,可将部分流量定向至专用API网关URL,部分流量定向至共享API网关URL。
在该模型中,每次处理请求时,我需要使用租户上下文来识别租户,并将这些请求路由到相应的网关URL。实现这种映射有多种方法。例如,如果租户使用子域名访问系统,可以使用HTTP请求头中的源来将租户映射到特定网关URL。在图6-10中,假设所有租户使用单一域名,因此需要通过租户管理服务(图右上角)查询每个租户的API网关URL。
此模型的缺点在于,它要求客户端参与映射过程。客户端必须从租户管理服务获取URL,并在发送请求时将该URL作为请求的一部分应用。
这种映射模型确实存在一些需要考虑的缺点。在每次请求中执行这些映射可能会增加开销和延迟,从而影响解决方案的性能。部分方案通过在客户端侧解析映射来解决此问题,但将此问题转移给客户端显得不够自然。更常见的做法是在映射层或网关层引入缓存策略,以存储最近映射的租户。关键在于确保将映射操作的性能影响纳入整体策略考量。
可扩展性也是需要关注的领域。在拥有大量租户的环境中,为每个租户设置网关可能无法良好扩展。此时,限制孤岛式租户资源的数量成为关键。如果只有少数高级租户而其余为基础租户,通常问题不大。然而,如果高级租户数量预计将大幅增长,则需重新评估方案。
容器租户路由
虽然无服务器路由示例更侧重于将租户映射到特定的API网关入口点,但接下来的策略更受无服务器应用程序在AWSLambda中的构建和部署本质驱动。为了对比,让我们看看在应用程序层主要基于Kubernetes的环境中,如何实现租户感知路由。
在Kubernetes多租户架构中,您拥有大量原生构造来塑造SaaS应用程序的路由足迹。可选方案非常丰富,但目前我将重点展示如何利用服务网格来构建此路由体验。如果您对服务网格的概念尚不熟悉,可以将其视为Kubernetes平台机制,允许您在应用程序外部配置/实现系统中的不同安全性和可观察性方面。存在多种服务网格实现方案。在本解决方案中,我选择使用Istio来实现我们的路由模型。
图6-11展示了一个使用服务网格控制身份验证和路由流向租户环境的多租户Kubernetes环境的概念视图。
图6-11
您将看到我们的租户使用本章前面描述的“每个租户一个子域名”模型从前门进入(步骤1)。在本例中,来自Web应用程序的请求通过网络负载均衡器发送,该均衡器提供了一个持久的端点,可以轻松地从Web应用程序引用(步骤2)。然后,请求通过网络负载均衡器到达Istio入口网关,该网关在Kubernetes集群中自己的命名空间中运行。正是这个网关协调并应用路由到我们的身份提供者以及应用程序微服务所需的所有策略。
假设在本例中,租户未经过身份验证。在这种情况下,网关将通过Envoy反向代理发送请求(步骤4),该代理将使用传入的源子域将身份验证请求路由到特定于租户的OIDC代理,该代理也运行在单独的Kubernetes命名空间中。每个OIDC代理都会将身份验证请求转发到相应的租户身份构造。这可以是独立的身份提供者,也可以是提供者内部独立的分组构造。
一旦您的租户用户通过身份验证,网关就可以将您的请求发送到应用程序的服务(步骤5)。在此示例中,您将看到我有两个租户在不同的Kubernetes命名空间中运行。这意味着我们的网关必须检查源并将每个请求路由到相应的租户命名空间。
虽然此解决方案包含许多可变部分,但对我来说,它仍然比那些依赖于服务查找和映射租户的策略更加优雅。在这里,我们可以依靠网关的自然路由和代理机制来重定向请求,从而将许多繁重的工作从微服务代码中移出。通常,如果您可以将这些路由职责卸载到已经管理路由的其他基础设施部分,这通常会带来更清晰、更易于管理的路由体验。
我在本文中介绍的身份验证和路由机制的细微差别,显然会受到多租户架构中是否存在资源孤岛的影响。我讨论了租户可能拥有单独身份结构的场景。我还讨论了应用程序平面中资源孤岛如何影响路由模型。虽然这些都是完全有效的策略,但您还必须考虑环境规模将如何影响您的身份验证和路由方法。您引入的单独租户结构越多,以支持特定于租户的身份模型或孤岛部署模式,您就越需要考虑这些结构如何根据系统中的租户数量有效地进行扩展。如果您要处理大量租户,某些概念可能与您的成本、规模或运营目标不太匹配。