2013-04-15 1 views
6

Ich schreibe eine Multi-Tenant-Anwendung mit einem One-Database-Pro-Tenant-Modell. Ich habe jedes Benutzerkonto erlaubt, mehrere Mieter ZugriffMulti-Tenant-Anwendung ermöglicht einem Benutzer den Zugriff auf viele Mandantenkonten?

Jede Seite an den Browser enthält in Site.Master die aktuelle TenantId

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

Aber wenn eine Anfrage gesendet (solange der Mieter sie Zugang gegeben hat) wird vom Browser aus gemacht (Übergabeschaltfläche, AJAX GET oder AJAX POST), diese TenantId wird NICHT tatsächlich geprüft, um festzustellen, ob sie mit der aktuellen TenantId des Benutzers übereinstimmt.

Nun, wenn der Benutzer eine Lasche geöffnet wird, mit TenantId = 1, dann mit TenantId in einem anderen Register zu einem anderen Mieter verbindet = 2 ist, schaltet dann auf die erste Lasche zurück und hat Zugriff auf Daten von Tenant 2.

Was kann ich tun, um das Problem zu beheben? Ich habe eine große Anzahl von vorhandenen Action und JsonResult Methoden und ich will nicht durch jeden von ihnen gehen und

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

hinzufügen Da, dass eine große Menge von duplizierten Aufwand wäre

Kann ich meine ändern Basis-Controller, um immer den Wert von TenantId zu lesen? Das könnte für gesendete Anfragen funktionieren (ActionResult), aber was ist mit AJAX-Anfragen?

Wie kann ich die TenantId der Seite innerhalb von JsonResult-Aktionen überprüfen, ohne jede einzelne vorhandene AJAX-Methode zu ändern (es gibt viele davon)?

Antwort

2

Sie könnten Ihre in der Application.Request-Ereignis in der Global.asax.cs-Datei überprüfen. Wenn das, was Sie benötigen, über die MVC-Modellbindung gefüllt wird, schreiben Sie vielleicht einen benutzerdefinierten ActionFilter, um ihn zu prüfen und ihn über einen GlobalFilter für alle Aktionen zu registrieren.

-1

Sie können einen Filter auf Controller-Ebene anwenden und die zu sendende Mandanten-ID prüfen. Controller-Level-Filter sollten nicht schwieriger als Action-Filter sein. Für mein Projekt musste ich die Autorisierung in ähnlicher Weise überprüfen, aber ich überging die Controller-Klasse und erbte dann von meiner eigenen Controller-Klasse, da ich einige sehr spezielle Bedürfnisse hatte.

Wohin werden Sie die Mandanten-ID auf der Client-Seite speichern? Es scheint mir, dass Sie ein Session-Objekt dafür verwenden sollten.

+0

Ich habe bereits eine Basis-Controller-Klasse, von der alle Controller erben. Wie kann ich damit einen Filter hinzufügen? Und das würde sicherlich nur ActionResult-Methoden betreffen (sie stammen aus Formularübergaben, so dass ich jedes der Formularfelder überprüfen kann), aber nicht JsonResult (weil diese AJAX-Submits sind und nur die tatsächlich übergebenen Felder enthalten, nicht das gesamte Formularsammlungsobjekt)) –

0

Sie können Sie eigene Filter schreiben:

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

Natürlich gibt es keine fertige Antwort für Sie Frage. Sie müssen Ihre eigene Logik schreiben, wie mandantId zu handhaben ist. Überprüfen Sie zum Beispiel bei jeder Aktion, ob sie nicht gleich ist, da die aktuelle Sitzungsmandanten-ID eine Umleitung vornimmt. Oder legen Sie es in einen Cookie und überprüfen Sie jedes Mal im Filter, ob die IDs gleich sind. Es liegt an dir. Aus meiner Sicht ist Cookie vorzuziehen. Aber es isst Verkehr.

0

Wenn ich richtig verstanden habe, könnten Benutzer gleichzeitig zwei verschiedene Registerkarten öffnen, jede mit einem anderen Mandanten. Und jede Seite sollte Daten anzeigen, die für jeden Mandanten relevant sind.

Das bedeutet also, dass eine Lösung mit einem Cookie oder der Sitzung verworfen werden muss, da der Tenant für jede Browser-Registerkarte spezifisch ist.

Und wenn ich Ihre Antwort auf Cyril Guptas Vorschlag lese, verstehe ich, dass die versteckte mententId auf jeder Seite nicht bei jeder AJAX-Anfrage eingereicht werden kann. Eine Lösung könnte natürlich sein, Ihre Anwendung zu modifizieren und sicherzustellen, dass dies bei jeder AJAX-Anfrage immer der Fall ist. Sonst würde das auch einen globalen Filter basierend auf den Anfrageparametern verwerfen, da die mandentId möglicherweise nicht immer da ist.

Ich denke, die beste Option ist dann, ein Segment in der URL hinzuzufügen, die die mandatorId enthält. Zum Beispiel die Standardroute mit so etwas wie die folgende Route zu ersetzen (Wenn Sie viele verschiedene Wege sein müssen Sie müssten Route Kollision sehr vorsichtig vermeiden): Sie können die Mieter machen kann, wird immer darauf geachtet werden

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

Auf diese Weise bei jeder Anfrage eingereicht werden, und Sie könnten auch 2 verschiedene Registerkarten mit verschiedenen Mandanten haben, die jeweils die entsprechenden Daten anzeigen.

Es gibt verschiedene Möglichkeiten, den Wert des Routensegments wiederherzustellen.

den Wert füllt automatisch auf jedem Parameter mit dem Namen „Mieter“ auf der Aktionsmethode Der Bindung wird oder über den Parameter „Mieter“ in einer Modellklasse mit dem Namen, der ein Parameter der Aktionsmethode ist: public Action Foo (FooModel Modell , Zeichenfolgenmandant) { // Sowohl mandant als auch model.tenant enthalten den Wert des URL-Segments return View(); }

Sie auch einen Filter schreiben könnte, die den Wert der Route Parameter zugreifen (Routedata ist eine Eigenschaft der Klasse ActionExecutingContext und ActionExecutedContext als Parameter der Filtermethoden erhalten), und eine gewisse Logik durchführt.

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 
    } 
} 

Die letzte Option ist, um direkt die Routedata-Parameter auf Basis Controller-Klasse zugreifen: Der Filter würde dann als globale Filter in Ihrer Anwendung oder auf Ihren Basisregler eingestellt wird. (Da RouteData eine Eigenschaft der Basisklasse MVC Controller ist)

Solange Sie HTML- und Ajax-Helfer für die URL-Generierung verwenden, wird das Tenant-Segment der URL in Ihren Links beibehalten. Wenn Sie jedoch jQuery-Code haben, der direkt AJAX-Aufrufe mit fest codierten URLs sendet, müssten Sie diesen Code aktualisieren, damit das neue URL-Segment berücksichtigt wird.

Wenn die mentId-Werte etwas nicht sehr benutzerfreundliches wie eine Ganzzahl sind, könnten Sie eindeutige Namen für jeden Mandanten haben und die Namen in der URL verwenden. Sie fügen dann eine Logik hinzu, die den Integer-Wert Ihrer Anwendung abbildet.

+0

Entschuldigung, um zu verdeutlichen: Ich brauche nicht beide Tabs gleichzeitig zu arbeiten. Ich brauche nur, dass der erste Tab sich weigert, weitere Daten zu laden, da der Benutzer nun im 2. Mieterkonto angemeldet ist. Auch möchte ich nicht die Mieter-ID in der URL haben, aber ich habe es in der Formularsammlung der Seite verfügbar, wenn das hilft. –

+0

@JK, dann könnten Sie einen ähnlichen Ansatz verfolgen, aber die mandentId von der Anfrage wiederherstellen (was im Grunde das ist, was die anderen bereits vorgeschlagen haben). Das Problem besteht darin, dass Sie sicherstellen müssen, dass die mandatorId in jeder AJAX-Anfrage gesendet wird –

Verwandte Themen