31 Stimmen

Anzeige verschiedener Inhalte innerhalb einer einzigen Ansicht basierend auf der Benutzerrolle

Angenommen, wir haben ein Menü innerhalb meiner Angular SPA-Anwendung. Jetzt möchte ich, dass die grundlegenden Optionen allen Benutzern angezeigt werden, wie z.B. Startseite, Über uns, Karrieremöglichkeiten usw.

Ich möchte auch einige andere Optionen haben, wie z.B. Benutzer verwalten, Beiträge verwalten usw., die nur für einen Administrator angezeigt werden sollen.

Angenommen, wir haben auch einen API-Zugriffspunkt, der mir die Benutzerrolle liefert oder besser noch, dass die Benutzerrolle im Objekt enthalten ist, das von /api/users/me abgerufen wurde.

Was wäre der beste Weg, um diese Verwaltungstools vor dem Anzeigen durch reguläre Benutzer zu schützen?

Gibt es eine Art Vererbung bei Ansichten? Wie bei Django? Gibt es eine Möglichkeit, die DOM-Elemente vor unberechtigten Benutzern zu verstecken? (Ja, ich weiß, dass es clientseitig ist.)

Ich würde wirklich lieber keine verschiedenen Ansichten für das Menü verwenden, da es ein generisches Komponent sein soll.

Ich nehme an, wenn die Antwort auf alle meine vorherigen Fragen nein ist, bleibt die Frage: Was ist die beste Implementierung dafür? Eine benutzerdefinierte Direktive ("E" + "A") zum Beispiel:

Bearbeiten Seite
Anzeigen Seite

oder vielleicht einfach ng-show mit einer Bedingung auf dem Benutzerobjekt verwenden?

39voto

Oleg Belousov Punkte 9823

Die Lösung ist in diesem Fiddle:

http://jsfiddle.net/BmQuY/3/

var app = angular.module('myApp', []);

app.service('authService', function(){

  var user = {};
  user.role = 'Gast';
  return{
    getUser: function(){
      return user;
    },
    generateRoleData: function(){
      /* Dies wird aufgelöst, bevor der
         Router die Ansicht und das Modell lädt.
         Es muss ein Promise zurückgeben. */
      /* ... */
    }
  }
});

app.directive('restrict', function(authService){
    return{
        restrict: 'A',
        priority: 100000,
        scope: false,
        compile:  function(element, attr, linker){
            var accessDenied = true;
            var user = authService.getUser();

            var attributes = attr.access.split(" ");
            for(var i in attributes){
                if(user.role == attributes[i]){
                    accessDenied = false;
                }
            }

            if(accessDenied){
                element.children().remove();
                element.remove();           
            }

            return function linkFn() {
                /* Optional */
            }
        }
    }
});

Wenn Sie diese Direktive mit IE 7 oder 8 verwenden möchten, müssen Sie die Kinder des Elements manuell entfernen, sonst wird ein Fehler ausgegeben:

  angular.forEach(element.children(), function(elm){
    try{
      elm.remove();
    }
    catch(ignore){}
  });

Beispiel für mögliche Verwendung:

Administrative Optionen

Unittest mit Karma + Jasmine: Achtung: Die done Rückruffunktion ist nur für Jasmine 2.0 verfügbar. Wenn Sie 1.3 verwenden, sollten Sie stattdessen waitsFor verwenden.

  describe('restrict-remove', function(){
    var scope, compile, html, elem, authService, timeout;
    html = '';
    beforeEach(function(){
      module('myApp.directives');
      module('myApp.services');
      inject(function($compile, $rootScope, $injector){
        authService = $injector.get('authService');
        authService.setRole('Gast');
        scope = $rootScope.$new();
        // compile = $compile;
        timeout = $injector.get('$timeout');
        elem = $compile(html)(scope);
        elem.scope().$apply();
      });
    });
    it('sollte eine einfache rollenbasierte Inhaltsdiskretion ermöglichen', function(done){
        timeout(function(){
          expect(elem).toBeUndefined(); 
          done(); //möglicherweise benötigt einen längeren Timeout;
        }, 0);
    });
  });
  describe('restrict-keep', function(){
    var scope, compile, html, elem, authService, timeout;
    html = '';
    beforeEach(function(){
      module('myApp.directives');
      module('myApp.services');
      inject(function($compile, $rootScope, $injector){
        authService = $injector.get('authService');
        timeout = $injector.get('$timeout');
        authService.setRole('admin');
        scope = $rootScope.$new();
        elem = $compile(html)(scope);
        elem.scope().$apply();
      });
    });

    it('soll Benutzern mit ausreichenden Berechtigungen ermöglichen, eingeschränkten Inhalte anzuzeigen', function(done){
      timeout(function(){
        expect(elem).toBeDefined();
        expect(elem.length).toEqual(1);
        done(); //möglicherweise benötigt einen längeren Timeout;
      }, 0)
    })
  });

Eine generische Zugriffskontrolldirektive für Elemente, ohne Verwendung von ng-if (nur seit V1.2 - derzeit instabil) oder ng-show, das das Element nicht tatsächlich aus dem DOM entfernt.

4voto

m59 Punkte 42516

ng-if ist definitiv der Weg, den ich gehen würde! Platziere einfach die Moderationstools im gesamten View, wo sie hingehören, und sie werden erscheinen, wenn der Benutzer sie haben sollte. ng-show/ng-hide sind auch in Ordnung, wenn du eine Version von Angular vor 1.1.5 verwendest.

Live-Demo! (hier klicken)

Es ist SEHR wichtig, sicherzustellen, dass dein Backend/Server/API keinen Request ehrt, nur weil dein JavaScript einen Moderatoraufruf gemacht hat!! Lasse den Server immer die Autorisierung bei jedem Aufruf validieren.

0voto

Jonathan Punkte 1

Ng-if oder ng-hide sind in Ordnung dafür,

das HTML ist lediglich eine Ansicht und sollte nicht für die Sicherheitsüberprüfung verantwortlich sein, deine Sicherheit

Es kann nützlich sein, ein Berechtigungsobjekt mit dem Benutzer zu verknüpfen. Wenn ich Benutzerdaten abrufe, erhalte ich ein Berechtigungs-JSON und dann im Angular

Sehr eingeschränkter Bereich

Und das Benutzer-JSON sieht so aus:

{name:"jemand", permissions:{restrictedArea:false, sandbox:true}} // und so weiter...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X