多租户应用程序允许一个用户访问多个租户账户吗?

7
我正在编写一个使用每个租户一个数据库模型的多租户应用程序。我允许每个用户帐户访问多个租户(只要该租户已授权给他们访问)。
每个发送到浏览器的页面都包括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请求呢?

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

4个回答

2
如果我理解正确的话,用户可以同时打开两个不同租户的标签页。每个页面都应显示与各自租户相关的数据。
这意味着涉及cookie或会话的解决方案需要被舍弃,因为租户是特定于每个浏览器标签页的。
阅读您对Cyril Gupta建议的回答后,我了解到每个页面上的隐藏tenantId可能不会在每个AJAX请求中提交。当然,一种解决方案是修改您的应用程序,并确保在每个AJAX请求中始终如此。否则,基于请求参数的全局过滤器也将被舍弃,因为tenantId可能不总是存在。
我认为最好的选择是在URL中添加一个包含tenantId的段落。例如,使用以下路由替换默认路由(如果您有许多不同的路由,则需要非常小心以避免路由冲突):
routes.MapRoute(
            name: "Default",
            url: "{tenant}/{controller}/{action}/{id}",
            defaults: new { tenant = "defaultTenant", controller = "Home", action = "Index", id = UrlParameter.Optional }
        );

这样,您可以确保每个请求都会提交租户,并且您还可以拥有两个不同的选项卡,显示各自相应的数据。
有不同的选项来恢复路由段的值。
绑定将自动填充任何命名为“tenant”的参数的值到您的操作方法中,或者填充任何模型类中作为操作方法参数的参数命名为“tenant”的值: public ActionResult Foo(FooModel model, string tenant) { //tenant和model.tenant都将包含URL段的值 return View(); }
您还可以编写一个过滤器,访问路由参数的值(RouteData是过滤器方法接收的ActionExecutingContext和ActionExecutedContext类的属性),并执行一些逻辑。然后将该过滤器设置为全局过滤器或基本控制器:
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.
@JK,你可以采用类似的方法,但需要从请求中获取tenantId(这基本上是其他人已经建议的)。问题在于,您需要确保在每个AJAX请求中都发送了tenantId。 - Daniel J.G.

2

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


0

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

如何在ASP.NET MVC 2中使某些代码在每个控制器操作之前执行? 在任何操作之前执行代码

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


-1

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

您打算在客户端存储租户ID吗?在我看来,您应该使用会话对象来完成这个任务。


我已经有一个基础控制器类,所有控制器都继承自它。我该如何使用它来添加过滤器?这只会影响ActionResult方法(它们来自表单提交,因此我可以检查任何表单字段),但不会影响JsonResult方法(因为这些是AJAX提交,它们只包含实际提交的字段,而不是整个表单集合对象)。 - JK.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接