Wo und von wem sollen in und MVC Benutzerberechtigungsprüfungen durchgeführt werden?

26

Sollten im Modell oder in der Steuerung Benutzerberechtigungsprüfungen stattfinden? Und wer sollte die Berechtigungsprüfungen, das User-Objekt oder einen UserManagement-Helfer durchführen?

Wo soll es passieren?

Einchecken des Controllers:

class MyController {
  void performSomeAction() {
    if (user.hasRightPermissions()) {
      model.someAction();
    }
  }
  ...

Wenn Sie die Prüfungen im Controller haben, können Sie die Aktionen der Models vereinfachen, sodass die gesamte Logik für die Controller erhalten bleibt.

Einchecken des Modells:

class MyModel {
  void someAction() {
    if (user.hasRightPermissions()) {
      ...
    }
  }
  ...

Indem wir die Checks in das Modell einfügen, erschweren wir das Modell, stellen aber auch sicher, dass Benutzer nicht versehentlich Dinge tun dürfen, die sie im Controller nicht tun sollen.

Und von wem?

Wer sollte die Kontrollen durchführen, wenn wir uns auf dem Platz entschieden haben? Der Nutzer?

Class User {
  bool hasPermissions(int permissionMask) {
    ...
  }
  ...

Aber es liegt nicht in der Verantwortung des Benutzers, zu wissen, auf was er oder sie zugreifen kann, also vielleicht eine Helferklasse?

Class UserManagement {
  bool hasPermissions(User user, int permissionMask) {
    ...
  }
  ...

Ich weiß, dass es üblich ist, nur eine einzige Frage in einer Frage zu stellen, aber ich denke, dass diese Fragen gut zusammen beantwortet werden können.

kba
quelle

Antworten:

20

Wie immer "kommt darauf an"

  • Berechtigungsprüfungen funktionieren überall dort, wo es bequem ist, sie zu platzieren.
  • Wenn Sie jedoch eine technische Frage stellen, lautet die Antwort möglicherweise "Setzen Sie die Prüfungen in das Objekt ein, das die für die Prüfung erforderlichen Daten besitzt" (dies ist wahrscheinlich der Controller).
  • Wenn Sie jedoch eine philosophische Frage stellen, schlage ich eine alternative Antwort vor: Zeigen Sie den Benutzern keine Aktionen, zu deren Ausführung sie nicht berechtigt sind .

In letzterem Fall können Sie die Berechtigungsprüfung im Controller als boolesche Eigenschaft implementieren und diese Eigenschaft an die Visible-Eigenschaft der Schaltfläche oder des Bedienfelds in der Benutzeroberfläche binden, die die Aktion steuert

Als Benutzer ist es frustrierend, Schaltflächen für Aktionen zu sehen, die ich nicht ausführen kann. fühlt sich an, als würde ich aus dem Spaß ausgeschlossen;)

Steven A. Lowe
quelle
Unsere Anwendung implementiert das dritte Szenario mit der Ausnahme, dass wir die Steuerelemente nicht verbergen, sondern deaktivieren. Leider ist alles in Winforms Code-Behind erledigt, so dass es für die OP-Frage nicht wirklich relevant ist.
Dave Nay
11
"Es ist frustrierend, Schaltflächen für Aktionen zu sehen, die ich nicht ausführen kann" -> Versuchen Sie, Ihren eigenen Beitrag zu verbessern :)
Rowan Freeman
5
Es reicht nicht aus, nur Schaltflächen für Aktionen auszublenden, die der Benutzer nicht ausführen kann. Der Server muss jede Anforderung nach Berechtigungen überprüfen. Das dritte Aufzählungszeichen ist keine "alternative Antwort", sondern kann zusätzlich zur Überprüfung der serverseitigen Berechtigungen verwendet werden.
Flimm
@Flimm hat zugestimmt, ob die Anfragen von einem Server bearbeitet werden; Die spezifische Frage betraf die Controller-Klasse
Steven A. Lowe
7

Sicherheit ist ein Querschnittsthema und muss daher in mehreren Ebenen implementiert werden. Was folgt, ist ein Beispiel für MVC, aber das Konzept gilt für andere Architekturen und / oder Muster. Sie müssen nur die Durchsetzungspunkte identifizieren.

Wo soll es passieren?

Ansichten können Benutzeroberflächenelemente (Widgets, Schaltflächen, Menüs usw.) enthalten, die je nach Berechtigung für einige Benutzer angezeigt werden müssen oder nicht. Dies liegt möglicherweise in der Verantwortung der Ansichts-Engine , da nicht jede Ansicht für sich alleine damit umgehen soll. Abhängig von der Art der Elemente, für die Sie eine Autorisierung durchführen, verlagern Sie diese Verantwortung an einen anderen Ort. Stellen Sie sich zum Beispiel ein Menü vor, in dem einige Elemente angezeigt werden müssen und andere nicht. Die Elemente können als Liste irgendwo implementiert werden und diese Liste basierend auf Berechtigungen filtern und dann an die Ansicht weiterleiten.

Controller reagieren auf Anforderungen. Wenn ein Benutzer nicht über die Berechtigung zum Ausführen einer Aktion verfügt, sollte dies überprüft werden, bevor die Aktion aufgerufen wird. Dabei wird die Verantwortung auf den Aktionsaufrufer übertragen, anstatt sie im Controller zu belassen . Dies hat den Vorteil, dass Ihr Controller sauber bleibt. Wenn sich die Berechtigungen ändern, müssen Sie Ihre Controller nicht durchsehen, um diese Änderungen zu übernehmen.

Ressourcen werden basierend auf Berechtigungen angezeigt. Dies erfolgt normalerweise auf Datenbankebene , da Sie nicht alles aus der Datenbank abrufen und dann Berechtigungen anwenden möchten.

Wie Sie sehen, gibt es je nach dem, was Sie autorisieren möchten, verschiedene Stellen, an denen dies durchgeführt werden sollte. Das Ziel ist es, so unauffällig wie möglich zu sein, damit Sie Ihre Sicherheitsrichtlinie bei Änderungen problemlos anwenden können, vorzugsweise ohne den Code Ihrer Anwendung zu ändern. Dies ist möglicherweise nicht für kleine Anwendungen gültig, bei denen der Berechtigungssatz relativ klein ist und sich nicht sehr oft ändert. In Unternehmensanwendungen ist die Geschichte jedoch ganz anders.

Wer soll das machen?

Ganz klar nicht das Modell. Jede Ebene sollte einen Durchsetzungspunkt haben, der die Autorisierung verwaltet. Der kursive Text oben hebt den möglichen Durchsetzungspunkt für jede Ebene hervor.

Schauen Sie sich XACML an . Sie müssen es nicht so implementieren, wie es ist, aber es gibt Ihnen einige Anweisungen, denen Sie folgen könnten.

devnull
quelle
Das ist die beste Antwort. Aus irgendeinem Grund befassen sich der Erste und andere mit den Unterschieden zwischen Controller und Ansicht oder Ansicht und Modell, was vom OP nicht verlangt wird. Vielen Dank!
RedFur
1

Ich benutze folgendes Schema. Es ist anzumerken, dass die meisten Überprüfungen von Benutzerberechtigungen in zwei allgemeine Fälle unterteilt werden können:

  • Benutzerzugriff auf die Controller-Aktion basierend auf der Benutzerrolle ohne Überprüfung der Parameter Die Aktion wird mit aufgerufen.
  • Benutzerzugriff auf das Modell basierend auf einer Logik oder Beziehung zwischen einem bestimmten Benutzer und einem bestimmten Modell.

Der Zugriff auf Controller-Aktionen ohne Überprüfung der Attribute wird normalerweise in MVC-Frameworks implementiert. Das ist ganz einfach: Sie definieren Regeln, Ihre Benutzer haben eine Rolle. Sie müssen lediglich überprüfen, ob der Benutzer über die Berechtigung zum Ausführen von Aktionen verfügt, und seine Rolle in Regeln nachschlagen.

Der Benutzerzugriff auf ein bestimmtes Modell sollte im Modell definiert werden. (Der Schauspieler ist die Basisbenutzerklasse. Angenommen, er kann entweder Kunde, Verkäufer oder Gast sein.)

interface ICheckAccess
{
    public function checkAccess(Actor $actor, $role);
}

class SomeModel implements ICheckAccess
{
    public function checkAccess(Actor $actor, $role)
    {
        // Your permissions logic can be as sophisticated as you want.
    }
}

Das Einfügen dieser Logik in das Modell bringt einen gewissen Gewinn. Die Zugriffsprüfungsmethode kann vererbt werden. Sie müssen keine zusätzlichen Klassen erstellen. Sie können die allgemeinen OOP-Vorteile nutzen.

Um die Zugriffskontrolle zu vereinfachen, werden einige Annahmen getroffen, die der Einfachheit und des guten Stils halber fast immer bereits implementiert sind:

  • In der Regel beziehen sich Steuerungen auf eine Modellklasse.
  • Aktionen, die auf Zugriff überprüft werden, verwenden eine einzelne Modell-ID als Parameter.
  • Auf diesen Parameter kann immer einheitlich über die Methode der Basis-Controller-Klasse zugegriffen werden.
  • Die Aktion wird in der Steuerung platziert, die dem Modell entspricht, das die ID-Aktion ausführt.

Mit diesen Annahmen können Aktionen, die die Modell-ID verwenden, einer bestimmten Modellinstanz zugeordnet werden. Tatsächlich können die meisten Aktionen leicht transformiert und verschoben werden, um den oben angegebenen Annahmen zu entsprechen.

Dann sollte eine abstrakte Basiscontrollerklasse definiert und vererbt werden.

abstract class ModelController
{
    // Retrieve model from database using id from action parameter.
    public abstract function loadModel($id);

    // Returns rules for user role to pass to SomeModel::checkAccess()
    // Something like array('view' => 'viewer', 'delete' => 'owner', 'update' => 'owner')
    public abstract function modelRules();

    public abstract fucntion getIdParameter();

    public function filterModelAccess()
    {
        $id = $this->getIdParameter();
        if(!$this->checkModelAccess($id))
            throw new HttpException(403);
    }

    public function checkModelAccess($id)
    {
        $model = $this->loadModel($id);
        $actor = My::app()->getActor();
        $rules = $this->modelRules();
        $role = $rules[My::app()->getActionName()];
        return $model->chechAccess($actor, $role);
    }
}

Sie können die Methode SomeController :: checkModelAccess ($ id) aufrufen, wenn Sie Ihre Menüs erstellen und entscheiden, ob ein Link angezeigt werden soll.

George Sovetov
quelle
Es tut mir leid für PHP.
George Sovetov
1

In Modell und Ansicht

In der Ansicht - weil die Benutzeroberfläche nicht die Benutzeroberflächenelemente anzeigen soll, die auf den aktuellen Benutzer beschränkt sind

(Zum Beispiel sollte die Schaltfläche "Löschen" für Personen mit entsprechenden Berechtigungen angezeigt werden.)

Im Modell - weil Ihre App wahrscheinlich eine Art API hat, oder? Die API muss ebenfalls die Berechtigungen überprüfen und verwendet das Modell möglicherweise erneut.

( Angenommen , Sie haben gleichzeitig die Schaltfläche "Löschen" in der Benutzeroberfläche und die API-Methode "http: / server / API / DeleteEntry / 123"

jitbit
quelle
Warum haben Sie das Modell dem Controller vorgezogen?
Flimm
Ich weiß nicht, warum ich es ansehe, modelliere und nicht in der Steuerung, wo es die meiste Zeit gemacht wird.
VP.
@VP Der Controller hat keine Möglichkeit, UI-Elemente anzuzeigen / auszublenden (außer ein Bool-Var
jitbit
Ich weiß nicht, normalerweise wird alles in der Controller-Ebene erledigt, deshalb war ich neugierig.
VP.
0

MVC ist ein Präsentationsmuster. Als solches sollten Ansicht und Kontrolleur nur Verantwortlichkeiten in Bezug auf die Darstellung haben. Einige Berechtigungen gelten für die Präsentation, z. B. ein Expertenmodus, experimentelle Benutzeroberflächenfunktionen oder unterschiedliche Designs. Diese können vom MVC-Controller verwaltet werden.

Viele andere Arten von Berechtigungen sind für mehrere Ebenen der Anwendung relevant. Wenn Sie beispielsweise Benutzer haben möchten, die nur Daten anzeigen und keine Änderungen vornehmen können:

  • Die Präsentationsebene muss die Bearbeitungsfunktionen verbergen
  • Wenn eine Bearbeitungsfunktion trotzdem aufgerufen wird, könnte / sollte dies erkannt werden (von den anwendungsspezifischen Teilen der Business-Schicht, nicht von dem domänenspezifischen Teil davon - TrainEditor, nicht von Train) und wahrscheinlich eine Ausnahme verursachen
  • Die Datenzugriffsebene kann auch nach Schreibvorgängen suchen, aber nach komplexeren Arten von Berechtigungen, für die schnell zu viel Wissen über die Business-Ebene erforderlich ist, um eine gute Idee zu haben.

Bei diesem Ansatz gibt es einige Überschneidungen. Da die Präsentation in der Regel flüchtig ist, kann man im normalerweise stabileren Teil der Anwendung eine gute Begründung für die Überprüfung der Berechtigung abgeben, auch wenn dies einige redundante Überprüfungen für den Fall bedeutet, dass die Präsentationsschicht wie beabsichtigt funktioniert.

Patrick
quelle