Erste Frage
Können Sie mir bitte erklären, wie die einfachste ACL in MVC implementiert werden kann?
Hier ist der erste Ansatz zur Verwendung von Acl in Controller ...
<?php
class MyController extends Controller {
public function myMethod() {
//It is just abstract code
$acl = new Acl();
$acl->setController('MyController');
$acl->setMethod('myMethod');
$acl->getRole();
if (!$acl->allowed()) die("You're not allowed to do it!");
...
}
}
?>
Es ist ein sehr schlechter Ansatz und das Minus ist, dass wir Acl-Code in die Methode jedes Controllers einfügen müssen, aber wir brauchen keine zusätzlichen Abhängigkeiten!
Der nächste Ansatz besteht darin, alle Methoden des Controllers zu private
erstellen und ACL-Code zur __call
Methode des Controllers hinzuzufügen .
<?php
class MyController extends Controller {
private function myMethod() {
...
}
public function __call($name, $params) {
//It is just abstract code
$acl = new Acl();
$acl->setController(__CLASS__);
$acl->setMethod($name);
$acl->getRole();
if (!$acl->allowed()) die("You're not allowed to do it!");
...
}
}
?>
Es ist besser als der vorherige Code, aber die Hauptminuspunkte sind ...
- Alle Methoden des Controllers sollten privat sein
- Wir müssen ACL-Code in die __call-Methode jedes Controllers einfügen.
Der nächste Ansatz besteht darin, Acl-Code in den übergeordneten Controller einzufügen, aber wir müssen weiterhin alle Methoden des untergeordneten Controllers privat halten.
Was ist die Lösung? Und was ist die beste Vorgehensweise? Wo soll ich Acl-Funktionen aufrufen, um zu entscheiden, ob eine Methode ausgeführt oder nicht zugelassen werden soll?
Zweite Frage
Bei der zweiten Frage geht es darum, mit Acl eine Rolle zu bekommen. Stellen wir uns vor, wir haben Gäste, Benutzer und Freunde des Benutzers. Der Benutzer hat nur eingeschränkten Zugriff auf die Anzeige seines Profils, die nur von Freunden angezeigt werden kann. Alle Gäste können das Profil dieses Benutzers nicht anzeigen. Also, hier ist die Logik ..
- Wir müssen sicherstellen, dass die aufgerufene Methode Profil ist
- Wir müssen den Besitzer dieses Profils ermitteln
- Wir müssen feststellen, ob der Betrachter Eigentümer dieses Profils ist oder nicht
- Wir müssen die Einschränkungsregeln für dieses Profil lesen
- Wir müssen entscheiden, ob die Profilmethode ausgeführt werden soll oder nicht
Die Hauptfrage betrifft die Erkennung des Profilbesitzers. Wir können erkennen, wer Eigentümer des Profils ist, indem wir nur die Modellmethode $ model-> getOwner () ausführen, aber Acl hat keinen Zugriff auf das Modell. Wie können wir das umsetzen?
Ich hoffe, dass meine Gedanken klar sind. Entschuldigung für mein Englisch.
Danke dir.
quelle
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(sonst "Sie haben keinen Zugriff auf das Profil dieses Benutzers" oder so etwas? Ich verstehe es nicht.Antworten:
Erster Teil / Antwort (ACL-Implementierung)
Meiner bescheidenen Meinung nach wäre der beste Weg, dies zu erreichen, die Verwendung eines Dekorationsmusters . Grundsätzlich bedeutet dies, dass Sie Ihr Objekt nehmen und es in ein anderes Objekt legen , das wie eine Schutzhülle wirkt. Dies würde NICHT erfordern, dass Sie die ursprüngliche Klasse erweitern. Hier ist ein Beispiel:
Und so würden Sie diese Art von Struktur verwenden:
Wie Sie vielleicht bemerken, hat diese Lösung mehrere Vorteile:
Controller
Es gibt jedoch auch ein Hauptproblem bei dieser Methode: Sie können nicht nativ überprüfen, ob gesicherte Objektimplemente und Schnittstellen (die auch zum Nachschlagen vorhandener Methoden gelten) oder Teil einer Vererbungskette sind.
Zweiter Teil / Antwort (RBAC für Objekte)
In diesem Fall besteht der Hauptunterschied darin, dass Ihre Domänenobjekte (im Beispiel :)
Profile
selbst Details zum Eigentümer enthalten. Dies bedeutet, dass Sie, um zu überprüfen, ob (und auf welcher Ebene) der Benutzer Zugriff darauf hat, diese Zeile ändern müssen:Im Wesentlichen haben Sie zwei Möglichkeiten:
Stellen Sie der ACL das betreffende Objekt zur Verfügung. Aber Sie müssen aufpassen, dass Sie nicht gegen das Gesetz von Demeter verstoßen :
Fordern Sie alle relevanten Details an und stellen Sie der ACL nur das zur Verfügung, was sie benötigt. Dadurch wird sie auch ein bisschen benutzerfreundlicher:
Paar Videos, die Ihnen helfen könnten, Ihre eigene Implementierung zu entwickeln:
Randnotizen
Sie scheinen das weit verbreitete (und völlig falsche) Verständnis dafür zu haben, was Modell in MVC ist. Modell ist keine Klasse . Wenn Sie eine Klasse mit dem Namen
FooBarModel
oder etwas, das erbtAbstractModel
, haben, machen Sie es falsch.In der richtigen MVC ist das Modell eine Ebene, die viele Klassen enthält. Ein großer Teil der Klassen kann je nach Verantwortung in zwei Gruppen unterteilt werden:
- Domain Business Logic
( lesen Sie mehr : hier und hier ):
Instanzen aus dieser Klasse von Klassen befassen sich mit der Berechnung von Werten, prüfen auf unterschiedliche Bedingungen, implementieren Verkaufsregeln und erledigen den Rest, was Sie als "Geschäftslogik" bezeichnen würden. Sie haben keine Ahnung, wie Daten gespeichert werden, wo sie gespeichert werden oder ob Speicher überhaupt vorhanden sind.
Das Domain Business-Objekt hängt nicht von der Datenbank ab. Wenn Sie eine Rechnung erstellen, spielt es keine Rolle, woher die Daten stammen. Es kann entweder aus SQL oder einer Remote-REST-API oder sogar aus einem Screenshot eines MSWord-Dokuments stammen. Die Geschäftslogik ändert sich nicht.
- Datenzugriff und Speicherung
Instanzen aus dieser Gruppe von Klassen werden manchmal als Datenzugriffsobjekte bezeichnet. Normalerweise Strukturen, die ein Data Mapper- Muster implementieren (nicht mit gleichnamigen ORMs verwechseln. Keine Beziehung). Hier befinden sich Ihre SQL-Anweisungen (oder möglicherweise Ihr DomDocument, weil Sie es in XML speichern).
Neben den beiden Hauptteilen gibt es noch eine Gruppe von Instanzen / Klassen, die erwähnt werden sollten:
- Dienstleistungen
Hier kommen Ihre und Komponenten von Drittanbietern ins Spiel. Sie können sich beispielsweise "Authentifizierung" als Dienst vorstellen, der von Ihnen selbst bereitgestellt werden kann, oder als externen Code. "Mail-Absender" wäre auch ein Dienst, der ein Domänenobjekt mit einem PHPMailer oder SwiftMailer oder Ihrer eigenen Mail-Absender-Komponente zusammenfügen könnte.
Eine weitere Quelle für Dienste ist die Abstraktion auf Domänen- und Datenzugriffsebenen. Sie werden erstellt, um den von Controllern verwendeten Code zu vereinfachen. Zum Beispiel: neue Benutzerkonten könnten zur Arbeit mit mehreren erfordert Domänenobjekte und Mapper . Bei Verwendung eines Dienstes werden jedoch nur ein oder zwei Leitungen in der Steuerung benötigt.
Was Sie bei der Erbringung von Dienstleistungen beachten müssen, ist, dass die gesamte Schicht dünn sein soll . Es gibt keine Geschäftslogik in Diensten. Sie dienen nur zum Jonglieren von Domänenobjekten, Komponenten und Zuordnungen.
Allen gemeinsam ist, dass Dienste die Ansichtsebene nicht direkt beeinflussen und so autonom sind, dass sie außerhalb der MVC-Struktur selbst verwendet werden können (und häufig beendet werden). Solche autarken Strukturen erleichtern auch die Migration auf ein anderes Framework / eine andere Architektur erheblich, da die Kopplung zwischen Service und dem Rest der Anwendung äußerst gering ist.
quelle
Request
Instanz (oder ein Analogon davon) initialisieren . Der Controller extrahiert nur Daten aus derRequest
Instanz und übergibt den größten Teil an die richtigen Dienste (ein Teil davon wird auch angezeigt). Dienste führen Vorgänge aus, zu denen Sie ihnen befohlen haben. Wenn view dann die Antwort generiert, fordert es Daten von Diensten an und generiert basierend auf diesen Informationen die Antwort. Diese Antwort kann entweder HTML sein, das aus mehreren Vorlagen erstellt wurde, oder nur ein HTTP-Speicherortheader. Hängt vom vom Controller eingestellten Status ab.ACL und Controller
Zuallererst: Dies sind meistens verschiedene Dinge / Schichten. Wenn Sie den beispielhaften Controller-Code kritisieren, werden beide zusammengefügt - am offensichtlichsten zu eng.
tereško hat bereits einen Weg aufgezeigt , wie Sie dies mit dem Dekorationsmuster mehr entkoppeln können.
Ich würde zuerst einen Schritt zurückgehen, um nach dem ursprünglichen Problem zu suchen, mit dem Sie konfrontiert sind, und das dann ein wenig diskutieren.
Einerseits möchten Sie Controller haben, die nur die Arbeit erledigen, die ihnen befohlen wurde (Befehl oder Aktion, nennen wir es Befehl).
Auf der anderen Seite möchten Sie ACL in Ihre Anwendung einfügen können. Das Arbeitsfeld dieser ACLs sollte - wenn ich Ihre Frage richtig verstanden habe - darin bestehen, den Zugriff auf bestimmte Befehle Ihrer Anwendungen zu steuern.
Diese Art der Zugangskontrolle benötigt daher etwas anderes, das diese beiden zusammenbringt. Basierend auf dem Kontext, in dem ein Befehl ausgeführt wird, wird ACL aktiviert und es muss entschieden werden, ob ein bestimmter Befehl von einem bestimmten Betreff (z. B. dem Benutzer) ausgeführt werden kann oder nicht.
Fassen wir bis zu diesem Punkt zusammen, was wir haben:
Die ACL-Komponente spielt hier eine zentrale Rolle: Sie muss mindestens etwas über den Befehl wissen (um den Befehl genau zu identifizieren) und den Benutzer identifizieren können. Benutzer sind normalerweise leicht durch eine eindeutige ID zu identifizieren. In Webanwendungen gibt es jedoch häufig Benutzer, die überhaupt nicht identifiziert werden, häufig als Gast, anonym, alle usw. bezeichnet. In diesem Beispiel wird davon ausgegangen, dass die ACL ein Benutzerobjekt verwenden und diese Details kapseln kann. Das Benutzerobjekt ist an das Anwendungsanforderungsobjekt gebunden und kann von der ACL verwendet werden.
Was ist mit der Identifizierung eines Befehls? Ihre Interpretation des MVC-Musters legt nahe, dass ein Befehl aus einem Klassennamen und einem Methodennamen besteht. Wenn wir genauer hinschauen, gibt es sogar Argumente (Parameter) für einen Befehl. Es ist also gültig zu fragen, was genau einen Befehl identifiziert. Der Klassenname, der Methodenname, die Anzahl oder die Namen der Argumente, sogar die Daten in einem der Argumente oder eine Mischung aus all dem?
Je nachdem, welchen Detaillierungsgrad Sie benötigen, um einen Befehl in Ihrer ACL zu identifizieren, kann dies sehr unterschiedlich sein. Lassen Sie es uns für das Beispiel einfach halten und angeben, dass ein Befehl durch den Klassennamen und den Methodennamen identifiziert wird.
Der Kontext, in dem diese drei Teile (ACL, Befehl und Benutzer) zueinander gehören, ist jetzt klarer.
Wir könnten sagen, mit einem imaginären ACL-Inhalt können wir bereits Folgendes tun:
Sehen Sie einfach, was hier passiert: Indem Sie sowohl den Befehl als auch den Benutzer identifizierbar machen, kann die ACL ihre Arbeit erledigen. Der Job der ACL hängt nicht mit der Arbeit des Benutzerobjekts und des konkreten Befehls zusammen.
Es fehlt nur ein Teil, dieser kann nicht in der Luft leben. Und das tut es nicht. Sie müssen also den Ort finden, an dem die Zugriffskontrolle aktiviert werden muss. Schauen wir uns an, was in einer Standard-Webanwendung passiert:
Um diesen Ort zu finden, müssen wir wissen, dass er vor der Ausführung des konkreten Befehls ausgeführt werden muss, damit wir diese Liste reduzieren können und nur die folgenden (potenziellen) Orte untersuchen müssen:
Irgendwann in Ihrer Anwendung wissen Sie, dass ein bestimmter Benutzer die Ausführung eines konkreten Befehls angefordert hat. Sie führen hier bereits eine Art ACL durch: Wenn ein Benutzer einen Befehl anfordert, der nicht vorhanden ist, lassen Sie diesen Befehl nicht ausführen. Wo immer dies in Ihrer Anwendung geschieht, ist dies möglicherweise ein guter Ort, um die "echten" ACL-Prüfungen hinzuzufügen:
Der Befehl wurde gefunden und wir können ihn identifizieren, damit die ACL damit umgehen kann. Falls der Befehl für einen Benutzer nicht zulässig ist, wird der Befehl nicht ausgeführt (Aktion). Möglicherweise konnte eine
CommandNotAllowedResponse
anstelle derCommandNotFoundResponse
für den Fall geltenden Anfrage nicht auf einen konkreten Befehl aufgelöst werden.Der Ort, an dem die Zuordnung einer konkreten HTTPRequest einem Befehl zugeordnet wird, wird häufig als Routing bezeichnet . Da das Routing bereits die Aufgabe hat, einen Befehl zu finden, können Sie ihn erweitern, um zu überprüfen, ob der Befehl tatsächlich pro ACL zulässig ist. ZB durch Erweitern des
Router
auf einen ACL-fähigen Router :RouterACL
. Wenn Ihr Router das noch nicht kenntUser
,Router
ist das nicht der richtige Ort, denn damit die ACL funktioniert, muss nicht nur der Befehl, sondern auch der Benutzer identifiziert werden. Dieser Ort kann also variieren, aber ich bin sicher, dass Sie den Ort, den Sie erweitern müssen, leicht finden können, da dieser Ort die Benutzer- und Befehlsanforderungen erfüllt:Benutzer ist von Anfang an verfügbar, Befehl zuerst mit
Request(Command)
.Anstatt Ihre ACL-Prüfungen in die konkrete Implementierung jedes Befehls einzufügen, platzieren Sie sie davor. Sie brauchen keine schweren Muster, Magie oder was auch immer, die ACL erledigt ihren Job, der Benutzer erledigt ihren Job und insbesondere der Befehl erledigt seinen Job: Nur der Befehl, sonst nichts. Der Befehl hat kein Interesse daran zu wissen, ob Rollen für ihn gelten oder nicht, ob er irgendwo bewacht ist oder nicht.
Halten Sie also einfach Dinge auseinander, die nicht zueinander gehören. Verwenden Sie eine leichte Umformulierung des Single Responsibility Principle (SRP) : Es sollte nur einen Grund geben, einen Befehl zu ändern - da sich der Befehl geändert hat. Nicht, weil Sie jetzt ACL'ing in Ihre Anwendung einführen. Nicht, weil Sie das Benutzerobjekt wechseln. Nicht, weil Sie von einer HTTP / HTML-Schnittstelle zu einer SOAP- oder Befehlszeilenschnittstelle migrieren.
Die ACL in Ihrem Fall steuert den Zugriff auf einen Befehl, nicht den Befehl selbst.
quelle
Eine Möglichkeit besteht darin, alle Ihre Controller in eine andere Klasse einzuschließen, die Controller erweitert, und alle Funktionsaufrufe nach Überprüfung der Autorisierung an die umschlossene Instanz zu delegieren.
Sie können dies auch vorgelagerter im Dispatcher tun (sofern Ihre Anwendung tatsächlich eine hat) und die Berechtigungen anhand der URLs anstelle der Steuermethoden nachschlagen.
Bearbeiten : Ob Sie auf eine Datenbank, einen LDAP-Server usw. zugreifen müssen, ist orthogonal zur Frage. Mein Punkt war, dass Sie eine Autorisierung basierend auf URLs anstelle von Controller-Methoden implementieren konnten. Diese sind robuster, da Sie Ihre URLs normalerweise nicht ändern (URLs-Bereich als öffentliche Schnittstelle), aber Sie können auch die Implementierungen Ihrer Controller ändern.
In der Regel verfügen Sie über eine oder mehrere Konfigurationsdateien, in denen Sie bestimmte URL-Muster bestimmten Authentifizierungsmethoden und Autorisierungsanweisungen zuordnen. Der Dispatcher bestimmt vor dem Versenden der Anforderung an die Controller, ob der Benutzer autorisiert ist, und bricht den Versand ab, wenn er dies nicht ist.
quelle