【问题标题】:Multi-tenant application allow one user to access many tenant accounts?多租户应用程序允许一个用户访问多个租户帐户?
【发布时间】:2013-04-24 04:30:10
【问题描述】:

我正在编写一个多租户应用程序,每个租户模型使用一个数据库。我已允许每个用户帐户访问多个租户(只要该租户授予他们访问权限)

发送到浏览器的每个页面都包含 Site.Master 中的当前 TenantId

<%= Html.Hidden("TenantId") %>

但是当浏览器发出任何请求(提交按钮、AJAX GET 或 AJAX POST)时,实际上不会检查此 TenantId 是否与用户当前的 TenantId 匹配。

现在,如果用户打开一个 TenantId = 1 的选项卡,然后连接到 TenantId = 2 的另一个选项卡中的另一个租户,然后切换回第一个选项卡,它就可以访问来自租户 2 的数据。

我能做些什么来解决这个问题?我有大量现有的 ActionResult 和 JsonResult 方法,我不想逐一检查并添加

if (request.TenantId != user.CurrentTenantId) return false

因为那将是大量的重复工作

我可以将我的基本控制器更改为始终读取 TenantId 的值吗?这可能适用于提交的请求(ActionResult),但是 AJAX 请求呢?

如何在不更改每个现有 AJAX 方法(有很多)的情况下检查 JsonResult 操作中页面的 TenantId?

【问题讨论】:

    标签: c# ajax asp.net-mvc multi-tenant jsonresult


    【解决方案1】:

    您可以检查 Global.asax.cs 文件中的 Application_Request 事件。如果您需要通过 MVC 模型绑定填充,则可以编写一个自定义 ActionFilter 来检查它并通过 GlobalFilter 将其注册到所有操作中。

    【讨论】:

      【解决方案2】:

      如果我理解正确,用户可以同时打开 2 个不同的选项卡,每个选项卡都有不同的租户。每个页面都应该显示与每个租户相关的数据。

      这意味着需要丢弃涉及 cookie 或会话的解决方案,因为租户特定于每个浏览器选项卡。

      阅读您对 Cyril Gupta 建议的回答后,我了解每个页面上隐藏的tenantId 可能不会在每个 AJAX 请求上提交。 当然,一种解决方案可能是修改您的应用程序并确保每个 AJAX 请求始终如此。 否则,这也会丢弃基于请求参数的全局过滤器,因为可能并不总是存在tenantId。

      我认为最好的选择是在包含tenantId 的URL 中添加一个段。 例如,将默认路由替换为以下路由(如果您有许多不同的路由,则需要非常小心以避免路由冲突):

      routes.MapRoute(
                  name: "Default",
                  url: "{tenant}/{controller}/{action}/{id}",
                  defaults: new { tenant = "defaultTenant", controller = "Home", action = "Index", id = UrlParameter.Optional }
              );
      

      这样,您可以确保每次请求都会提交租户,并且您还可以有 2 个不同的选项卡,其中不同的租户显示每个相应的数据。

      关于如何恢复路线段的价值有不同的选择。

      绑定将自动填充操作方法上任何名为“tenant”的参数的值,或模型类中作为操作方法参数的任何名为“tenant”的参数的值: 公共 ActionResult Foo(FooModel 模型,字符串租户) { //tenant 和 model.tenant 都将包含 URL 段的值 返回视图(); }

      您还可以编写一个过滤器来访问路由参数的值(RouteData 是作为过滤器方法的参数接收的ActionExecutingContextActionExecutedContext 类的属性),并执行一些逻辑。然后过滤器将在您的应用程序或您的基本控制器中设置为全局过滤器:

      public class FooFilterAttribute : ActionFilterAttribute
      {
          public override void OnActionExecuting(ActionExecutingContext filterContext)
          {
              var tenant = filterContext.RouteData.Values["tenant"]
              //do whatever you need to do before executing the action, based on the tenant 
          }
      
          public override void OnActionExecuted(ActionExecutedContext filterContext)
          {
              var tenant = filterContext.RouteData.Values["tenant"]
              //do whatever you need to do after executing the action, based on the tenant
          }
      }
      

      最后一个选项是直接访问基础控制器类上的 RouteData 参数。 (因为 RouteData 是基础 MVC Controller 类的属性)

      只要您使用 Html 和 Ajax 帮助器生成 URL,URL 的租户段就会保留在您的链接中。但是,如果您有 jquery 代码直接发送带有硬编码 URL 的 AJAX 调用,则需要更新该代码以便考虑新的 url 段。

      最后,如果tenantId 值不是非常用户友好的值(例如整数),您可以为每个租户设置唯一名称并使用URL 中的名称。然后,您将添加一些逻辑,将其映射到您的应用程序所需的整数值。

      【讨论】:

      • 抱歉,澄清一下:我不需要两个选项卡同时工作。我只需要第一个选项卡拒绝加载任何进一步的数据,因为用户现在已登录到第二个租户帐户。此外,我不想在 URL 中包含租户 ID,但如果有帮助,我确实可以在页面的表单集合中使用它。
      • @JK,那么您可以采用类似的方法,但从请求中恢复tenantId(这基本上是其他人已经建议的)。问题是您需要确保在每个 AJAX 请求中都发送了tenantId
      【解决方案3】:

      您可以编写自己的过滤器:

      How do I get certain code to execute before every single controller action in ASP.NET MVC 2? Executing code before any action

      当然,您的问题没有现成的答案。您需要编写自己的逻辑,如何处理tenantId。例如,在每个操作上,检查它是否不等于当前会话租户 ID 进行重定向。或者把它放在 cookie 中,每次在过滤器中检查 id 是否相等。由你决定。从我的角度来看,cookie 更可取。但它会吃掉流量。

      【讨论】:

        【解决方案4】:

        您可以在控制器级别应用过滤器并检查正在发送的租户 ID。控制器级别的过滤器不应该比操作过滤器更难。对于我的项目,我需要以类似的方式检查授权,但我覆盖了控制器类,然后从我自己的控制器类继承,因为我有一些非常特殊的需求。

        您打算将租户 ID 存储在客户端的什么位置?在我看来,您应该使用会话对象来执行此操作。

        【讨论】:

        • 我已经有一个所有控制器都继承自的基本控制器类。如何使用它来添加过滤器?当然这只会影响 ActionResult 方法(它们来自表单提交,所以我可以检查任何表单的字段),但不会影响 JsonResult 方法(因为这些是 AJAX 提交,它们只包含实际提交的字段,而不是整个表单集合对象)
        猜你喜欢
        • 1970-01-01
        • 2018-06-03
        • 1970-01-01
        • 1970-01-01
        • 2014-11-01
        • 1970-01-01
        • 2013-06-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多