Standardpraktiken für die Zugangskontrolle (Entwurfsmuster)

9

Ich bin an meinem Interface - Design suchen , und ich kämpfe , um zu entscheiden , welche die „richtige“ Art und Weise ist die rollenbasierte Zugriffssteuerung zu implementieren, einen gegebenen userund ein , subjectdass die für userden Zugriff möchte.


Soweit ich sehen kann, habe ich drei Kernoptionen (wobei eine vierte eine Bastardisierung der ersten drei und eine fünfte eine Optimierung der vierten ist):

  1. Fragen Sie das subjectmit einer Liste von Berechtigungen ab, die das userhat -subject.allowAccess(user.getPermissionSet)
  2. Fragen Sie die usermit einer Liste der Berechtigungen ab, die subjecterforderlich sind -user.hasPermissionTo(subject.getRequiredPermissions())
  3. Fragen Sie einen Drittanbieter ab, um die Schnittpunkte der Berechtigungen zu ermitteln. accessController.doPermissionSetsIntersect(subject.permissionSet, user.getPermissionSet())
  4. Fragen Sie entweder das subject/ ab user, während Sie die "Entscheidung" an eine Drittanbieterklasse delegieren
  5. Haben Sie den userVersuch , die für den Zugriff auf subjectund wirft einen Fehler , wenn der Zugriff nicht erlaubt ist

Ich neige zu Option vier - Lassen Sie das Feld subjectenthalten accessController, in dem Aufrufe zum subject.userMayAccess(User user)Delegieren der Operation a la:

class Subject {
    public function display(user) {
        if(!accessController.doPermissionSetsIntersect(this.permissionSet, user.getPermissionSet())) {
            display403(); //Or other.. eg, throw an error..
        }
    }
}

.. aber dann wirft dies weitere Fragen auf:

  • sollte das accessControllerein Feld gegen eine statische Klasse sein ..?
  • Sollte jemand subject wissen, welche Berechtigungen erforderlich sind, um es anzeigen zu können?
  • Wo kommt hier das Prinzip des geringsten Wissens in Bezug auf die Berufung ins Spiel subject.display()? Sollten Anrufer subject.display()jemals wissen, dass die Zugriffskontrolle wirksam ist? (Wo subject.display()ist eine endgültige "Vorlagenmethode")
  • Haben Sie subject.display()die Zugriffskontrolle verwaltet und eine Ausnahme ausgelöst, bei der der Benutzer nicht über die erforderliche Berechtigung verfügt?

Was würde in dieser Situation als "Best Practice" angesehen? Wo sollte die Verantwortung für die Durchführung der Prüfungen tatsächlich liegen?

Da dies sowohl eine akademische Übung als auch eine Umsetzung ist, sind Verweise auf Entwurfsmuster willkommen.

Kwah
quelle

Antworten:

7

Am besten verwenden Sie das sogenannte Interceptor-Muster, um Anrufe in geschützte Bereiche abzufangen.

Dies kann durch die Verwendung von AOP oder Querschnittsthemen erreicht werden, die auf Ihre Zugangspunkte angewendet werden.

Das Thema sollte niemals wissen, wer es sehen kann. Dies verkompliziert den Betreffcode unnötig und es gibt keinen Grund dafür, dass er erforderlich ist, es sei denn, Sie stellen versehentlich einen direkten Zugriffsmechanismus auf dieselbe Funktion bereit.

Vorzugsweise sollten der Anrufer und der Angerufene nichts über den Zugang wissen, abgesehen von der Behandlung von Ablehnungen. Das Problem hängt jedoch von dem System ab, auf dem Sie implementieren, und davon, wie Sie Zugriff auf die Sicherheitsanmeldeinformationen / -prinzipien für den Anrufer erhalten. In SOAP-Systemen werden diese Informationen beispielsweise dem Header einer SOAP-Nachricht hinzugefügt, während sie in einem Windows-System über den Windows-Authentifizierungsmechanismus verfügbar wären.

Wenn Sie den AOP- oder Interceptor-Pattern-Ansatz verwenden, werden alle erforderlichen Ausnahmen ausgelöst, und es ist Sache des Clients (Aufrufers), alle ausgelösten Ausnahmen zu behandeln.

Auf diese Weise halten Sie Ihren Client-, Service- und Authentifizierungscode getrennt, ohne dass sich Wissen oder Funktionalität vermischen.

sweetfa
quelle
2

Ich denke, Ihre Option 3 ist die nächstgelegene, aber anstatt die userund subjectüber ihre Berechtigungssätze abzufragen, sollten Sie das userund subjectan den Zugriffscontroller übergeben.

class Subject {
    public function display(user) {
        if(!accessController.checkAccess(this, user, AccessControl.Read)) {
            display403(); //Or other.. eg, throw an error..
        }
    }
}

Der Zugriffscontroller sollte sowohl dafür verantwortlich sein, die Berechtigungssätze abzurufen als auch zu überprüfen, ob der Zugriff ausreichend ist. Auf diese Weise isolieren Sie sowohl die Speicherlogik als auch die Überprüfungslogik in Ihrem Zugriffscontroller, getrennt von Benutzer und Subjekt.

Das andere Element, das in Ihrem Beispiel möglicherweise fehlt, ist, welche Operation ausgeführt wird. Einige Benutzer können das Recht haben , einige Daten zu lesen , aber nicht zu aktualisieren, zu löschen, führen usw. So haben Sie eine haben könnten checkAccessMethode auf den Access - Controller mit drei Parametern: user, subject, operation. Möglicherweise checkAccessmöchten Sie auch einige zusätzliche Informationen zurückgeben , um Informationen darüber zurückzugeben, warum der Zugriff nicht gewährt wurde.

Wenn Sie dies beispielsweise später an den Zugriffscontroller delegieren, können Sie die Darstellung Ihrer Berechtigungen ersetzen. Sie können mit der rollenbasierten Zugriffskontrolle beginnen und später zur anspruchsbasierten wechseln. Sie können Berechtigungen zunächst in einer einfachen Struktur speichern und später hierarchische Gruppen / Rollen und zulässige Operationen für verschiedene Arten von Themen hinzufügen. Wenn Sie Ihre Berechtigungssätze nicht in die Benutzeroberfläche einfügen, können Sie dies aktivieren.

Ob Sie AOP oder einen Boilerplate-Code verwenden, um dies anzuschließen, ist meiner Meinung nach weniger wichtig.

Rory
quelle