10

Ich habe eine ASP.NET MVC-Anwendung mit Autorisierungsattributen auf Controllern und Aktionen. Dies hat gut funktioniert, aber eine neue Falte hat sich gezeigt.ASP.NET MVC: Autorisierung innerhalb einer Aktion - Vorgeschlagene Muster oder das ist ein Geruch?

Objekt: Versand

Rollen: Versand, Buchhaltung, General User

Der Versand über einen Workflow bewegt. Im Zustand A kann es nur mit Versand bearbeitet werden. Im Zustand B kann es nur vom Rechnungswesen bearbeitet werden.

Ich habe eine ShipmentController und eine Edit Action. Ich kann ein Autorisierungsattribut setzen, um die Edit-Aktion auf nur diese beiden Rollen zu beschränken, aber dies unterscheidet nicht zwischen dem Status der Sendung. Ich müsste innerhalb der Aktion vor dem Serviceaufruf eine Autorisierung durchführen, um festzustellen, ob der Benutzer vorhanden ist ist wirklich berechtigt, die Bearbeitungsaktion auszuführen.

Also mit zwei Fragen, die ich bin links:

1) Was ist ein guter Weg Genehmigung innerhalb einer Aktion zu haben. Die Controller-Aktion ruft einen Service auf, und der Service führt dann entsprechende Aufrufe an das Sendungsobjekt aus (Aktualisierungsmenge, Aktualisierungsdatum usw.). Ich weiß ganz sicher, dass das Versandobjekt unabhängig von Autorisierungsanforderungen sein soll. Auf der anderen Seite habe ich kein wirkliches Verständnis, wenn ich möchte, dass das Dienstobjekt über die Berechtigung informiert wird oder nicht. Gibt es dafür gute Muster?

2) Ist mein Problem tatsächlich ein Symptom für schlechtes Design? Anstelle von ShipmentController sollte ich einen StateAShipmentController und einen StateBShipmentController haben? Ich habe keinen Polymorphismus im Shipment-Objekt (der Status ist nur ein Enum), aber vielleicht sollte ich das und vielleicht sollten die Controller das reflektieren.

Ich denke, ich bin nach allgemeineren Lösungen, nicht eine bestimmte für meinen Fall. Ich wollte nur ein Beispiel geben, um die Frage zu veranschaulichen.

Danke!

+0

Warum nicht einen eigenen [Authorize] -ähnlichen Aktionsfilter für den Versand erstellen? –

+0

Erstellen Sie Ihre eigenen [Autorisieren] Filter wäre eine wirklich, wirklich schlechte Idee, und es ist eine, die das ASP.NET MVC-Entwickler-Team hat wirklich wirklich wirklich davon abgeraten –

+0

@Josh; Ich bin nicht einverstanden. Es ist keine "wirklich, wirklich schlechte Idee", es ist nur eine, die mit der gebotenen Sorgfalt angegangen werden sollte. Ich habe keine öffentlichen Artikel über Entmutigung gesehen, geschweige denn mit 'wirklich' und 'stark'. Das einzige wirkliche "Gotcha" von meinem Wissen ist, dass Sie von dem vom Framework bereitgestellten Authorize-Attribut erben möchten, weil Sie auf diese Weise sicherstellen können, dass es zuverlässig an der richtigen Stelle ausgelöst wird. – Paul

Antwort

2

Ihr Autorisierungsattribut konnte die Sendung aus Aktionsparametern oder Routendaten abrufen und dann eine Entscheidung treffen.

Für Nummer 1 gibt es eine Reihe von Mustern, die reiches Verhalten in Domänenobjekten ermöglichen. Bei Double-Dispatch übergeben Sie einen Verweis auf eine Service-Abstraktion (Schnittstelle) an eine Methode auf dem Objekt. Dann kann es sein Ding machen. Sie können auch einen Anwendungsdienst schreiben, der die Sendung übernimmt und die Arbeit erledigt.

Auf Nummer 2, nicht unbedingt. Möglicherweise müssen Sie das Konzept einer "kontextabhängigen Sendung" in einen Service abstrahieren, der herausfindet, in welchem ​​Sendungskontext Sie sich befinden. Aber ich würde das so lange sagen, bis Sie es wieder brauchen.

1

Sie könnten einen Blick auf Rhino.Security werfen, es könnte verwendet werden, um Benutzerautorisierung in diesen Arten von Szenarien zu implementieren.

3

Ich sehe kein Problem mit einer Aktionsmethode, die weitere Berechtigungsprüfungen darin durchführt. Sie können den Rollenanbieter für die gewünschte feinkörnige Autorisierung nutzen. Bitte entschuldige meine Syntax hier - es ist wahrscheinlich grob und ich habe das nicht getestet.

[Authorize(Roles="Shipping, Accounting")] 
public ActionResult Edit(int id) 
{ 
    Shipment shipment = repos.GetShipment(id); 


    switch (shipment.State) 
    { 
     case ShipmentState.A: 
     if (Roles.IsUserInRole("Shipping")) 
       return View(shipment); 
     else 
       return View("NotAuthorized"); 
     break; 
     case ShipmentState.B: 
     if (Roles.IsUserInRole("Accounting")) 
       return View(shipment); 
     else 
       return View("NotAuthorized"); 
     break; 
     default: 
       return View("NotAuthorized"); 
    } 
} 
+0

Während das eine "einfache" Lösung ist, riecht es ein bisschen. Hardcoding der Autorisierung auf dem Controller verletzt die SRP, und sollte zumindest in seine eigene Klasse refaktoriert werden. –

+0

Dieses Beispiel schreit geradezu nach einer Zustandsmusterimplementierung. – Paul

+1

heh ja, es ist sicher! So simpel es war, ich wollte sicherstellen, dass ich die Frage des OP beantwortete, anstatt mich in Details zu verlieren. –

1

Neben der Antwort oben können Sie eine HttpUnauthorizedResult zurückkehren, anstatt Ihre eigene Sicht für NotAuthorized zu schaffen.Dies wird auf die Anmeldeseite umleiten und verhält sich genau wie das übliche Attribut [Autorisieren]