将请求映射到多租户解决方案中的租户
每当请求到达应用程序时,都需要确定 租户上下文,即发出请求的租户。 如果具有可能托管在不同地理区域中的特定于租户的基础结构,则需要将传入请求与租户匹配。 然后,必须将请求转发到托管该租户资源的物理基础结构,如下所示:
许多多租户应用程序也具有基于用户的权限。 租户映射是一个单独的活动。 你需要知道正在发出请求的用户以及他们在其中工作的租户。
本文提供有关可考虑将请求映射到相应租户的方法的技术决策者的指导,以及方法中涉及的权衡。
注释
此页面主要讨论基于 HTTP 的应用程序,如网站和 API。 但是,许多相同的基本原则适用于使用其他通信协议的多租户应用程序。
标识租户的方法
有多种方法可以标识传入请求的租户。 每个方法都需要检查传入请求的某些方面。
域名
如果使用 特定于租户的域或子域名称,则可以通过使用 Host
标头、 X-Forwarded-Host
标头或其他包含每个请求的原始主机名的 HTTP 标头轻松地映射到租户。
但是,请考虑以下问题:
- 用户将如何知道访问服务的域名?
- 你是否有一个中心入口点(如登陆页面或登录页)是所有租户使用的? 如果你这样做,用户将如何选择他们正在使用的租户?
- 你使用哪些其他信息来验证对租户资源的访问,例如授权令牌? 授权令牌是否包含特定于租户的域名?
HTTP 请求属性
如果不使用特定于租户的域名,则仍可以使用 HTTP 请求的各个方面来标识特定请求所针对的租户。 例如,请考虑将租户名称标识为 tailwindtraders
的 HTTP 请求。 这可以使用以下方法之一进行通信:
-
URL 路径结构,例如
https://app.contoso.com/tailwindtraders/
。 - URL 中的查询字符串,例如
https://contoso.com/app?tenant=tailwindtraders
。 -
自定义 HTTP 请求标头,例如
Tenant-Id: tailwindtraders
。
重要
从 Web 浏览器发出 HTTP GET 请求或某些类型的 Web 代理处理请求时,自定义 HTTP 请求标头不起作用。 只有在生成 API 时,才应对 GET作使用自定义 HTTP 标头,或者控制发出请求的客户端,并且请求处理链中不包含任何 Web 代理,这些代理可能会修改或去除标头。
使用此方法时,应考虑以下问题:
- 用户会知道如何访问该服务吗? 例如,如果使用查询字符串来标识租户,中心登录页是否需要通过添加查询字符串将用户定向到正确的租户页面?
- 你是否有一个中心入口点(如登陆页面或登录页)是所有租户使用的? 如果你这样做,用户将如何选择他们需要访问的租户?
- 应用程序是否提供 API? 例如,Web 应用程序是单页应用程序(SPA)还是具有 API 后端的移动应用程序? 如果是,则可以使用 API 网关 或 反向代理 来执行租户映射。
令牌声明
许多应用程序使用基于声明的身份验证和授权协议,例如 OAuth 2.0 或 SAML。 这些协议向客户端提供授权令牌。 令牌包含一组声明,这些 声明是有关客户端应用程序或用户的信息片段。 声明可用于传达用户的电子邮件地址等信息。 然后,系统可以包含用户的电子邮件地址、查找用户到租户的映射,然后将请求转发到相应的部署。 或者,甚至可以在标识系统中包括租户映射,并向令牌添加租户 ID 声明。
如果使用声明将请求映射到租户,应考虑以下问题:
- 你会使用声明来标识租户吗? 将使用哪个声明?
- 用户是否可以是多个租户的成员? 如果可能,用户将如何选择要处理特定请求的租户?
- 是否有所有租户的中央身份验证和授权系统? 否则,如何确保所有令牌颁发一致的令牌和声明?
API 密钥
许多应用程序都会公开 API。 这些内容可能供组织内部使用,或者供合作伙伴或客户外部使用。 API 的常见身份验证方法是使用 API 密钥。 API 密钥随每个请求一起提供。 如果记录颁发密钥的租户 ID,则可以在使用密钥时查找租户 ID。
注释
API 密钥不提供高级别的安全性,因为它们需要手动创建和管理,它们是长期且经常重复使用的,因为它们不包含声明。 更现代且安全的方法是使用具有短生存期令牌(如 OAuth 2.0 或 OpenID Connect)的基于声明的授权机制。
可以通过多种方式生成 API 密钥。 下面是两种常见方法:
- 生成一个加密随机值,并将其与租户 ID 一起存储在查阅表中。 收到请求后,系统会在查找表中查找 API 密钥,然后将其与租户 ID 匹配。
- 创建一个有意义的字符串,其中包含一个租户 ID。 使用 HMAC 等方法对密钥进行数字签名。 处理每个请求时,请验证签名,然后提取租户 ID。
考虑以下问题:
- 如何生成和颁发 API 密钥?
- API 客户端如何安全地接收和存储 API 密钥?
- 是否需要 API 密钥在一段时间后过期? 如何在不造成停机的情况下轮换客户端的 API 密钥?
- 客户生成的 API 密钥是否为 API 提供足够的安全级别?
注释
API 密钥与密码不同。 API 密钥必须由系统生成,并且它们在所有租户中必须是唯一的,以便每个 API 密钥可以唯一映射到单个租户。 API 网关(例如 Azure API 管理)可以生成和管理 API 密钥、验证传入请求上的密钥,并将密钥映射到单个 API 订阅者。
客户端证书
客户端证书身份验证(有时称为相互 TLS(mTLS)通常用于服务到服务通信,以及未经身份验证的用户使用的无人参与设备或展台。 客户端证书提供了一种安全的方式来对客户端进行身份验证。 与令牌和声明类似,客户端证书提供可用于确定租户 的属性 。 例如,证书 的主题 可能包含用户的电子邮件地址,可用于查找租户。
计划将客户端证书用于租户映射时,请考虑以下事项:
- 如何安全地颁发和续订服务信任的客户端证书? 客户端证书可能非常复杂,因为它们需要特殊的基础结构来管理和颁发证书。 如果处理不当,这些 复杂性可以减少安全性 ,而不是增加安全性。
- 客户端证书是否仅用于初始登录请求,还是附加到服务的所有请求?
- 当你拥有大量客户端时,颁发和管理证书的过程是否会变得不可管理?
- 如何实现客户端证书和租户之间的映射?
反向代理
反向代理(也称为应用程序代理)可用于路由 HTTP 请求。 反向代理接受来自入口终结点的请求,并且可以将请求转发到多个后端终结点之一。 反向代理对多租户应用程序很有用,因为它们可以在某些请求信息之间执行映射,从而从应用程序基础结构卸载任务。
许多反向代理可以使用请求的属性来决定租户路由。 他们可以检查目标域名、URL 路径、查询字符串、HTTP 标头,甚至令牌或请求正文的一部分内的声明。
Azure 中使用以下常见反向代理:
- Azure Front Door 是一个全局负载均衡器和 Web 应用程序防火墙。 它使用Microsoft全局边缘网络来创建快速、安全且高度可缩放的 Web 应用程序。 可以使用 规则集 提取租户标识符,并将其放入请求的另一部分。
- Azure 应用程序网关 是一个托管的 Web 流量负载均衡器,部署到与后端服务相同的物理区域。
- Azure API 管理 针对 API 流量进行优化。 Azure API 管理提供了全面的 策略引擎 ,为从请求中提取租户信息提供了极大的灵活性。
- 商业和开源技术(你自己托管)包括 nginx、Traefik 和 HAProxy。
请求验证
应用程序必须验证它接收的任何请求是否已获得租户的授权。 例如,如果应用程序使用自定义域名将请求映射到租户,则应用程序仍必须检查应用程序收到的每个请求是否获得该租户的授权。 尽管请求包含域名或其他租户标识符,但这并不意味着应自动授予访问权限。 使用 OAuth 2.0 时,通过检查 受众 和 范围 声明来执行验证。
注释
这是 Microsoft Azure Well-Architected Framework 中假定零信任安全设计原则的一部分。
实现请求验证时,应考虑以下事项:
- 如何授权向应用程序发出所有请求? 无论使用哪种方法将请求映射到物理基础结构,都需要授权请求。
- 使用受信任的、广泛使用且维护良好的身份验证和授权框架和中间件,而不是自行实现所有验证逻辑。 例如,不要生成令牌签名验证逻辑或客户端证书加密库。 请改用已验证和测试的应用程序平台(或已知受信任包)的功能。
性能
租户映射逻辑可能会对应用程序的每个请求运行。 考虑随着解决方案的增长,租户映射过程将如何缩放。 例如,如果将数据库表作为租户映射的一部分进行查询,数据库是否支持大量负载? 如果租户映射需要解密令牌,计算要求是否会随着时间的推移而变得过高? 如果流量相当温和,则这不会影响整体性能。 但是,如果具有大规模应用程序,则此映射所涉及的开销可能会变得很大。
会话 Cookie
减少租户映射逻辑的性能开销的一种方法是使用 会话 Cookie。 请考虑仅在每个会话的第一个请求上计算信息,而不是对每个请求执行映射。 然后,应用程序会向客户端提供会话 Cookie。 客户端将会话 Cookie 传递回服务,并在该会话中发出所有后续客户端请求。
注释
Azure 中的许多网络和应用程序服务可能会发出会话 Cookie。
考虑以下问题:
- 是否可以使用会话 Cookie 来降低将请求映射到租户的开销?
- 使用哪些服务将请求路由到每个租户的物理部署? 这些服务是否支持基于 Cookie 的会话?
租户迁移
租户通常需要作为 租户生命周期的一部分移动到新的基础结构。 将租户移动到新部署时,他们访问的 HTTP 终结点可能会更改。 发生这种情况时,请考虑租户映射过程需要更改。 可能需要考虑以下因素:
- 如果应用程序对映射请求使用域名,则在迁移时可能还需要 DNS 更改。 DNS 更改可能需要时间才能传播到客户端,具体取决于 DNS 服务中 DNS 条目的生存时间(TTL)。
- 如果迁移更改迁移过程中任何终结点的地址,请考虑暂时将租户的请求重定向到自动刷新的维护页。
供稿人
本文由Microsoft维护。 它最初是由以下贡献者撰写的。
主要作者:
- 丹尼尔·斯科特-伦斯福德|高级合作伙伴技术解决方案顾问
其他参与者:
- John Downs | 首席软件工程师
- Paolo Salvatori | FastTrack for Azure 首席客户工程师
- 阿森·弗拉基米尔斯基 | 客户首席工程师,FastTrack for Azure
要查看非公开的 LinkedIn 个人资料,请登录到 LinkedIn。