使用 multi-tenant 数据访问的 Web API 身份验证

Web API Authentication with multi-tenant data access

我不确定我是否使用了正确的标题,但我想不出更好的方式来描述它。这可能更像是一个设计问题。

我有一个 multi-tenant 数据库,其中一个用户可以属于一个或多个实体。我通过调用 /token 端点使用 his/her 凭据对用户进行身份验证。

收到令牌后,我调用自己的端点(使用令牌)来获取该用户的可用实体列表,然后允许该用户在内存缓存中设置他的当前实体。然后我在内存缓存中使用它来查找所有后续请求的 entity/tenant ID,以了解调用数据库时用户 "logged into" 是哪个 entity/tenant。

理想情况下,我想通过将 entity/tenant ID 作为令牌中的声明来消除对内存缓存的需求,以使我的应用程序更加无状态,但我只在用户拥有后才知道此 ID经过身份验证并选择了 his/her 个实体。我显然不能在令牌发布后更改或添加到声明中,但是是否有替代设计来实现这种行为?

我考虑过可能为每个租户使用一个子域,但从技术上讲,这更难设置和维护。我还考虑过提示用户输入实体 he/she 希望使用他们的凭据作为自由文本登录,但这并不理想。

有没有人遇到过这个挑战?

我同意你的观点,即租户选择必须以无状态方式完成,以避免 REST-based 架构中的有状态交互。缓存方法可能会导致许多陷阱并引入对 session-based 交互的强烈依赖。

我可以想到两个主要选项来使租户选择无状态:

  1. 基于 URI 的选择:您可以添加 API 租户参数,在所有控制器之间共享。类似于此的映射:api/{tenantId}/{controller}/{id} 可以让您以简单的方式在租户之间切换 client-side。这可以使用 convention-based 路由来完成:

    routes.MapHttpRoute(
        name: "API Default",
        routeTemplate: "api/{tenantId}/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
    

    或 attribute-based 路由,在您的控制器上使用 RoutePrefixAttribute

    [RoutePrefix("api/{tenantId}/myentities")]
    public class EntityController : ApiController
    {
        [Route("")]
        public IHttpActionResult GetAllEntities(string tenantId)
        {
            //...
    
  2. Header 基于选择:您可以考虑添加自定义 HTTP header,其中将包含有关所选租户的信息:例如X-Tenant-Selection: TenantId,那么你可以阅读这个 Header inside a custom Filter,或者在 Web API 之前执行的 OwinMiddleware 中,并设置一个上下文变量(甚至是用户声明将持续当前请求)以在您的控制器中使用。

当然,在这两种情况下,您都必须在返回数据之前验证当前用户是否可以访问所选租户。

坦率地说,我会排除将 tenantId 包含在访问令牌中的可能性,sub-domain 方法对我来说太复杂了。

其实我不确定租户应该以无状态的方式实现。租户可能是身份验证的一部分,因此它应该保持在状态中(完全是用户)。如果租户是身份验证的一部分,我们应该能够像我们为用户所做的那样从 Bearer 中检索租户。

在我的例子中,我将用户存储在租户中,存储在一个租户中的 joe 不同于存储在另一个租户中的 joe。所以,当服务端收到Bearer时,不仅要获取用户名,还需要获取租户。

我已经写了一个概念证明。它缺少组管理(因此您不能在控制器中使用属性来检查组授权)。

已在此处发布
https://github.com/bubibubi/OAuth2CustomImplementation/