Ich versuche, ein benutzerdefiniertes Berechtigungsattribut in ASP.NET Core zu erstellen. In früheren Versionen war es möglich, zu überschreibenbool AuthorizeCore(HttpContextBase httpContext)
. Dies existiert aber nicht mehr in AuthorizeAttribute
.
Wie wird derzeit ein benutzerdefiniertes AuthorizeAttribute erstellt?
Was ich versuche zu erreichen: Ich erhalte eine Sitzungs-ID in der Header-Autorisierung. Anhand dieser ID weiß ich, ob eine bestimmte Aktion gültig ist.
Antworten:
Der vom ASP.Net Core-Team empfohlene Ansatz besteht darin, das neue Richtliniendesign zu verwenden, das hier vollständig dokumentiert ist . Die Grundidee des neuen Ansatzes besteht darin, das neue Attribut [Authorize] zu verwenden, um eine "Richtlinie"
[Authorize( Policy = "YouNeedToBe18ToDoThis")]
zu bestimmen (z. B. wenn die Richtlinie in Startup.cs der Anwendung registriert ist, um einen Codeblock auszuführen (dh sicherzustellen, dass der Benutzer einen Altersanspruch hat) wo das Alter 18 oder älter ist).Das Richtliniendesign ist eine großartige Ergänzung des Frameworks, und das ASP.Net Security Core-Team sollte für seine Einführung gelobt werden. Das heißt, es ist nicht für alle Fälle gut geeignet. Der Nachteil dieses Ansatzes besteht darin, dass er keine bequeme Lösung für die häufigste Notwendigkeit bietet, einfach zu behaupten, dass ein bestimmter Controller oder eine bestimmte Aktion einen bestimmten Anspruchstyp erfordert. In dem Fall, in dem eine Anwendung möglicherweise über Hunderte von diskreten Berechtigungen für CRUD-Operationen für einzelne REST-Ressourcen verfügt ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder" usw.), erfordert der neue Ansatz entweder wiederholte Einzelaufgaben eine Zuordnung zwischen einem Richtliniennamen und einem Anspruchsnamen (z
options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));
) oder Schreiben von Code, um diese Registrierungen zur Laufzeit durchzuführen (z. B. alle Anspruchstypen aus einer Datenbank lesen und den oben genannten Aufruf in einer Schleife ausführen). Das Problem bei diesem Ansatz besteht in den meisten Fällen darin, dass kein unnötiger Overhead erforderlich ist.Während das ASP.Net Core Security-Team empfiehlt, niemals eine eigene Lösung zu erstellen, ist dies in einigen Fällen die umsichtigste Option, um damit zu beginnen.
Das Folgende ist eine Implementierung, die den IAuthorizationFilter verwendet, um eine einfache Möglichkeit zum Ausdrücken einer Anspruchsanforderung für einen bestimmten Controller oder eine bestimmte Aktion bereitzustellen:
quelle
new ForbidResult()
funktioniert nicht (verursacht Ausnahme / 500) , weil es keine zugehörige Zulassungssystem hat. Was würde ich für diesen Fall verwenden?Ich bin die asp.net-Sicherheitsperson.
Lassen Sie mich zunächst entschuldigen, dass noch nichts davon außerhalb des Beispiels oder der Komponententests des Musikgeschäfts dokumentiert ist und alles noch in Bezug auf exponierte APIs verfeinert wird.Eine ausführliche Dokumentation finden Sie hier .Wir möchten nicht, dass Sie benutzerdefinierte Berechtigungsattribute schreiben. Wenn Sie das tun müssen, haben wir etwas falsch gemacht. Stattdessen sollten Sie die Berechtigung schreiben Anforderungen .
Die Autorisierung wirkt sich auf Identitäten aus. Identitäten werden durch Authentifizierung erstellt.
Sie sagen in Kommentaren, dass Sie eine Sitzungs-ID in einem Header überprüfen möchten. Ihre Sitzungs-ID wäre die Grundlage für die Identität. Wenn Sie das
Authorize
Attribut verwenden möchten, schreiben Sie eine Authentifizierungs-Middleware, um diesen Header in eine authentifizierte zu verwandelnClaimsPrincipal
. Sie würden dies dann innerhalb einer Autorisierungsanforderung überprüfen. Die Autorisierungsanforderungen können so kompliziert sein, wie Sie möchten. Hier ist beispielsweise eine, bei der ein Geburtsdatum für die aktuelle Identität angegeben wird und die autorisiert wird, wenn der Benutzer über 18 Jahre alt ist.Dann
ConfigureServices()
würden Sie es in Ihrer Funktion verkabelnUnd schließlich wenden Sie es auf einen Controller oder eine Aktionsmethode mit an
quelle
ManageStore
Anforderung aus dem Music Store". Da es sich um ein Beispiel handelt, gibt es nur die Möglichkeit, "alles oder nichts zuzulassen". Müssen wir dann für jede mögliche Permutation eine neue Richtlinie erstellen? dh "Benutzer / Lesen", "Benutzer / Erstellen", "Benutzer / Zuweisungsrolle", "Benutzer / Löschen", wenn wir feinkörnige Ansprüche wünschen? Klingt nach ziemlich viel Einrichtungsarbeit, um es zum Laufen zu bringen, und nach einer Fülle von Richtlinien, nur um Ansprüche zu verwalten, anstatt um ein[ClaimsAutzorization("User", "Read", "Create", "Delete", "Assign")]
Attribut?[CustomAuthorize(Operator.And, Permission.GetUser, Permission.ModifyUser)]
. Ich könnte ein einzelnes benutzerdefiniertes Attribut auf unendlich viele Arten verwenden, indem ich einfach die Konstruktorparameter ändere.IAuthorizeData.Policy
) und benutzerdefinierte Richtlinienanbieter zu verwenden, um dieses offensichtliche Versehen zu überwinden, anstatt es innerhalb des Frameworks anzusprechen. Ich dachte, wir sollten keine eigenen Implementierungen erstellen? Sie haben einigen von uns keine andere Wahl gelassen, als die Autorisierung (erneut) von Grund auf neu zu implementieren, und diesmal ohne den Vorteil des altenAuthorize
Attributs der Web-API . Jetzt müssen wir es auf der Ebene des Aktionsfilters oder der Middleware tun.Es scheint, dass Sie mit ASP.NET Core 2 erneut erben
AuthorizeAttribute
können. Sie müssen lediglich Folgendes implementierenIAuthorizationFilter
(oderIAsyncAuthorizationFilter
):quelle
OnAuthorization
Implementierung auf eine asynchrone Methode warten muss, diese implementieren sollten,IAsyncAuthorizationFilter
anstatt dassIAuthorizationFilter
Ihr Filter synchron ausgeführt wird und Ihre Controller-Aktion unabhängig vom Ergebnis des Filters ausgeführt wird.Basierend auf Derek Greer GREAT Antwort, habe ich es mit Aufzählungen.
Hier ist ein Beispiel für meinen Code:
quelle
Sie können Ihren eigenen AuthorizationHandler erstellen, der benutzerdefinierte Attribute auf Ihren Controllern und Aktionen findet, und diese an die HandleRequirementAsync-Methode übergeben.
Anschließend können Sie es für alle benutzerdefinierten Attribute verwenden, die Sie für Ihre Controller oder Aktionen benötigen. Zum Beispiel, um Berechtigungsanforderungen hinzuzufügen. Erstellen Sie einfach Ihr benutzerdefiniertes Attribut.
Erstellen Sie dann eine Anforderung, die Sie Ihrer Richtlinie hinzufügen möchten
Erstellen Sie dann den AuthorizationHandler für Ihr benutzerdefiniertes Attribut und erben Sie den zuvor erstellten AttributeAuthorizationHandler. Es wird eine IEnumerable für alle Ihre benutzerdefinierten Attribute in der HandleRequirementsAsync-Methode übergeben, die von Ihrem Controller und Ihrer Aktion gesammelt wurde.
Fügen Sie schließlich in Ihrer Startup.cs ConfigureServices-Methode Ihren benutzerdefinierten AuthorizationHandler zu den Diensten hinzu und fügen Sie Ihre Richtlinie hinzu.
Jetzt können Sie Ihre Controller und Aktionen einfach mit Ihrem benutzerdefinierten Attribut dekorieren.
quelle
Einfach: Erstellen Sie keine eigenen
AuthorizeAttribute
.Für reine Autorisierungsszenarien (z. B. Einschränkung des Zugriffs nur auf bestimmte Benutzer) wird empfohlen, den neuen Autorisierungsblock zu verwenden: https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84 -L92
Für die Authentifizierung wird am besten auf Middleware-Ebene gehandhabt.
Was versuchst du genau zu erreichen?
quelle
Wenn jemand nur ein Inhaber-Token in der Autorisierungsphase anhand der aktuellen Sicherheitspraktiken validieren möchte, können Sie
Fügen Sie dies Ihren Startup / ConfigureServices hinzu
und das in deiner Codebasis,
Wenn der Code nicht erreicht
context.Succeed(...)
wird, schlägt er trotzdem fehl (401).Und dann können Sie in Ihren Controllern verwenden
quelle
Der moderne Weg ist AuthenticationHandlers
in startup.cs hinzufügen
IUserService ist ein Dienst, den Sie erstellen, wenn Sie Benutzername und Kennwort haben. Im Grunde gibt es eine Benutzerklasse zurück, auf die Sie Ihre Ansprüche abbilden.
Dann können Sie diese Ansprüche abfragen und alle Daten, die Sie zugeordnet haben, es gibt einige, werfen Sie einen Blick auf die ClaimTypes-Klasse
Sie können dies in einer Erweiterungsmethode verwenden, um eine der Zuordnungen abzurufen
Dieser neue Weg ist meiner Meinung nach besser als
quelle
Zum jetzigen Zeitpunkt glaube ich, dass dies mit der IClaimsTransformation-Schnittstelle in asp.net Core 2 und höher erreicht werden kann. Ich habe gerade einen Proof of Concept implementiert, der gemeinsam genutzt werden kann, um hier zu posten.
Um dies in Ihrem Controller zu verwenden, fügen Sie einfach eine entsprechende
[Authorize(Roles="whatever")]
Methode zu Ihren Methoden hinzu.In unserem Fall enthält jede Anforderung einen Autorisierungsheader, der ein JWT ist. Dies ist der Prototyp, und ich glaube, wir werden nächste Woche in unserem Produktionssystem etwas sehr Nahes tun.
Zukünftige Wähler berücksichtigen das Datum des Schreibens, wenn Sie abstimmen. Ab heute
works on my machine.
möchten Sie wahrscheinlich mehr Fehlerbehandlung und Protokollierung Ihrer Implementierung.quelle
Zur Autorisierung in unserer App. Wir mussten einen Dienst basierend auf den im Berechtigungsattribut übergebenen Parametern aufrufen.
Wenn wir beispielsweise überprüfen möchten, ob der angemeldete Arzt Patiententermine anzeigen kann, übergeben wir "View_Appointment" an das benutzerdefinierte Berechtigungsattribut und überprüfen dieses Recht im DB-Service und basierend auf den Ergebnissen, die wir autorisieren. Hier ist der Code für dieses Szenario:
Und bei API-Aktionen verwenden wir es folgendermaßen:
quelle
Die akzeptierte Antwort ( https://stackoverflow.com/a/41348219/4974715 ) ist nicht realistisch wartbar oder geeignet, da "CanReadResource" als Anspruch verwendet wird (sollte aber im Wesentlichen eine Richtlinie in der Realität sein, IMO). Der Ansatz bei der Antwort ist in der Art und Weise, wie er verwendet wurde, nicht in Ordnung. Wenn für eine Aktionsmethode viele verschiedene Anspruchseinstellungen erforderlich sind, müssten Sie mit dieser Antwort wiederholt so etwas wie ...
Stellen Sie sich also vor, wie viel Codierung das kosten würde. Im Idealfall sollte "CanReadResource" eine Richtlinie sein, die viele Ansprüche verwendet, um zu bestimmen, ob ein Benutzer eine Ressource lesen kann.
Was ich tue, ist, dass ich meine Richtlinien als Aufzählung erstelle und dann die Anforderungen wie folgt durchlaufe und einrichte ...
Die DefaultAuthorizationRequirement-Klasse sieht aus wie ...
Beachten Sie, dass der obige Code auch die Vorabzuordnung eines Benutzers zu einer Richtlinie in Ihrem Datenspeicher ermöglichen kann. Wenn Sie also Ansprüche für den Benutzer erstellen, rufen Sie im Grunde genommen die Richtlinien ab, die dem Benutzer direkt oder indirekt vorab zugeordnet wurden (z. B. weil der Benutzer einen bestimmten Anspruchswert hat und dieser Anspruchswert identifiziert und einer Richtlinie zugeordnet wurde, z dass es eine automatische Zuordnung für Benutzer bietet, die auch diesen Anspruchswert haben) und die Richtlinien als Ansprüche eintragen, sodass Sie im Autorisierungshandler einfach überprüfen können, ob die Ansprüche des Benutzers eine Anforderung enthalten. Richtlinie als Wert eines Anspruchselements in ihrem Ansprüche. Dies dient dazu, eine Richtlinienanforderung statisch zu erfüllen, z. B. ist die Anforderung "Vorname" recht statisch. Damit,
Bei einer dynamischen Anforderung kann es sich um die Überprüfung des Altersbereichs usw. handeln, und Richtlinien, die solche Anforderungen verwenden, können Benutzern nicht vorab zugeordnet werden.
Ein Beispiel für die dynamische Überprüfung von Richtlinienansprüchen (z. B. um zu überprüfen, ob ein Benutzer älter als 18 Jahre ist) finden Sie bereits in der Antwort von @blowdart ( https://stackoverflow.com/a/31465227/4974715 ).
PS: Ich habe das auf meinem Handy eingegeben. Verzeihen Sie Tippfehler und fehlende Formatierung.
quelle