我使用每个租户模型一个数据库编写多租户应用程序。我允许每个用户帐户访问多个租户(只要该租户已授予他们访问权限)
发送到浏览器的每个页面都包含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(有很多方法)?
答案 0 :(得分:2)
您可以在Global.asax.cs文件中检查您的Application_Request事件。如果您需要通过MVC模型绑定填充,那么可以编写一个自定义ActionFilter来检查它并通过GlobalFilter将其注册到所有操作。
答案 1 :(得分:0)
您可以编写自己的过滤器:
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更受欢迎。但它吃了流量。
答案 2 :(得分:0)
如果我理解正确,用户可以同时打开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”的任何参数的值,或者作为action方法的参数的模型类中名为“tenant”的任何参数: 公共ActionResult Foo(FooModel模型,字符串租户) { // 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中的名称。然后,您将添加一些逻辑,将其映射到应用程序所需的整数值。
答案 3 :(得分:-1)
您可以在控制器级别应用过滤器并检查正在发送的tenantid。控制器级过滤器不应该比动作过滤器困难。对于我的项目,我需要以类似的方式检查授权,但我覆盖了控制器类,然后从我自己的控制器类继承,因为我有一些非常特殊的需求。
您打算在哪里将租户ID存储在客户端?在我看来,你应该使用会话对象来做到这一点。