Irgendwann benötigt eine Anwendung Autorisierung. Das bedeutet, dass verschiedene Zugriffsstufen sich auf einer Website unterschiedlich verhalten (oder überall sonst). Es kann alles sein, von der Anzeige von Daten bis zu ganzen Bereichen, auf die eine Gruppe von Benutzern keinen Zugriff hat.
In nicht Single Page Applications (SPA) wird ein Claim oder eine Rolle mit Daten oder einem Bereich der Anwendung verknüpft. Entweder hat der Benutzer diese Rolle oder diesen Claim oder nicht. In einer SPA ist es dasselbe, aber mit einem großen Vorbehalt. Eine SPA wird in den Browser heruntergeladen. An diesem Punkt hat der Browser die vollständige Kontrolle über den Code. Eine böswillige Person kann den Code ändern, um ihn für ihre Zwecke zu nutzen.
Da SPAs nicht gesichert werden können, sind Authentifizierung und Autorisierung in einer SPA einfach eine Benutzererfahrung. Alle aussagekräftigen Sicherheitsmaßnahmen müssen auf dem Webserver durchgeführt werden. Dieser Artikel behandelt nicht die Sicherung Ihrer API gegen Angriffe. Ich empfehle, sich ein Video von Pluralsight anzusehen oder ein Papier zu lesen, das sich mit der Sicherheit für Ihre Servertechnologie befasst.
Der Zweck dieses Artikels ist es, Ihnen zu zeigen, wie ich eine Autorisierungs-Benutzererfahrung zu meiner Angular 1.x SPA hinzugefügt habe.
Sicherheitsbereiche
Ich habe 3 Bereiche der Benutzeroberfläche identifiziert, die Autorisierung benötigen: Elemente (HTML), Routen und Daten.
Nur zur Erinnerung: Die Sicherung einer SPA ist kein Ersatz für die Sicherung des Servers. Berechtigungen auf dem Client dienen einfach dazu, ehrliche Menschen ehrlich zu halten und dem Benutzer eine gute Erfahrung zu bieten.
Die 3 Bereiche im Detail:
Elemente
Sie müssen bestimmte HTML-Elemente ausblenden. Es könnte sich um ein Label, eine Tabelle mit Daten, eine Schaltfläche oder ein beliebiges Element auf der Seite handeln.
Routen
Sie möchten ganze Routen ausblenden. In bestimmten Fällen möchten Sie nicht, dass der Benutzer auf eine Ansicht zugreift. Durch die Sicherung der Route kann ein Benutzer nicht zur Ansicht navigieren. Stattdessen wird ihm eine Meldung “Sie sind nicht berechtigt, zu dieser Ansicht zu navigieren” angezeigt.
Daten
Manchmal reicht es nicht aus, die Elemente in der Ansicht auszublenden. Ein aufmerksamer Benutzer kann einfach die Quelle anzeigen und die versteckten Daten in der HTML-Quelle sehen oder beobachten, wie sie in den Browser gestreamt werden. Was wir wollen, ist, dass die Daten überhaupt nicht abgerufen werden.
Das Hinzufügen von Sicherheit ist knifflig. Zunächst versuchte ich, den Zugriff auf der HTTP-API (auf dem Client) einzuschränken. Ich erkannte schnell, dass dies nicht funktionieren würde. Ein Benutzer hat möglicherweise keinen direkten Zugriff auf die Daten, aber das bedeutet nicht, dass er keinen indirekten Zugriff auf die Daten hat. Auf der HTTP-API-Ebene (normalerweise eine der niedrigsten in der Anwendung) können wir den Kontext des Aufrufs nicht bestimmen und daher keine Sicherheitsbedenken darauf anwenden.
Unten habe ich Codebeispiele bereitgestellt:
Code
Ich habe einen Service für den Autorisierungsprüfungscode erstellt. Dies ist das Herzstück der Autorisierung. Alle Autorisierungsanfragen verwenden diesen Service, um zu überprüfen, ob der Benutzer für die bestimmte Aktion berechtigt ist.
angular.module('services')
.service('AuthorizationContext',function(_, Session){
this.authorizedExecution = function(key, action){
//Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
Session.claims(function(claims){
var claim = findKey(key, claims);
//If Claim was found then execute the call.
//If it was not found, do nothing
if(claim !== undefined){
action();
}
});
};
this.authorized = function(key, callback){
//Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
Session.claims(function(claims){
var claim = findKey(key, claims);
//If they don't have any security key, then move forward and authorization.
var valid = claim !== undefined;
callback(valid);
});
};
//this.agencyViewKey = '401D91E7-6EA0-46B4-9A10-530E3483CE15';
function findKey(key, claims){
var claim = _.find(claims, function(item){
return item.value === key;
});
return claim;
}
});
Authorize-Direktive
Die Authorize-Direktive kann auf jedes HTML-Element angewendet werden, das Sie vor Benutzern ohne eine bestimmte Zugriffsstufe verbergen möchten. Wenn der Benutzer das Zugriffstoken als Teil seiner Claims hat, darf er das Element sehen. Wenn nicht, wird es vor ihm verborgen.
angular.module('directives')
.directive('authorize', ['$compile', 'AuthorizationContext', function($compile, AuthorizationContext) {
return {
restrict: 'A',
replace: true,
//can't have isolated the scope in a shared directive
link:function ($scope, element, attributes) {
var securityKey = attributes.authorize;
AuthorizationContext.authorized(securityKey, function(authorized){
var el = angular.element(element);
el.attr('ng-show', authorized);
//remove the attribute, otherwise it creates an infinite loop.
el.removeAttr('authorize');
$compile(el)($scope);
});
}
};
}]);
Elemente
Ich verlasse mich stark auf Registerkarten in meiner Anwendung. Ich wende die Authorize-Direktive auf die Registerkarte an, die ich vor Benutzern ohne die entsprechenden Claims verbergen möchte.
<tabset>
<tab ng-cloak heading="Users" authorize="{{allowUserManagement}}">
...html content
</tab>
</tabset>
Routen
Ich verwende ui-router. Leider habe ich für diejenigen, die dies nicht tun, keinen Code für den Standard-AngularJS-Router.
In $stateChangeStart authentifiziere ich die Route. Dies ist der Code in diesem Ereignis.
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
AuthenticationManager.authenticate(event, toState, toParams);
});
Die Funktion, die die Route autorisiert. Wenn sie autorisiert ist, darf die Route fortgesetzt werden. Wenn sie nicht autorisiert ist, wird dem Benutzer eine Meldung angezeigt und er wird zur Startseite weitergeleitet.
function authorizedRoute(toState, location, toaster, breadCrumbs){
if(toState.authorization !== undefined){
AuthorizationContext.authorized(toState.authorization, function(authorized){
if(!authorized){
toaster.pop('error', 'Error', 'You are not authorized to view this page.');
location.path("/search");
} else {
breadCrumbs();
}
});
} else{
breadCrumbs();
}
}
In dieser Router-Definition werden Sie eine Eigenschaft namens ‘authorization’ bemerken. Wenn der Benutzer diesen Claim hat, darf er fortfahren.
angular.module('agency',
[
'ui.router',
'services'
])
.config(function config($stateProvider){
$stateProvider.state( 'agency', {
url: '/agency',
controller: 'agency.index',
templateUrl: 'agency/agency.tpl.html',
authenticate: true,
authorization:'401d91e7-6ea0-46b4-9a10-530e3483ce15',
data:{ pageTitle: 'Agency' }
});
});
Daten
In einigen Fällen möchten Sie keine Anfrage an den Server für die Daten stellen. Wenn der Benutzer den Claim hat, darf er die Anfrage stellen.
Der obige AuthorizationContext am Anfang des Artikels zeigt den Code für authoriedExecution. Hier sehen Sie seine Verwendung.
AuthorizationContext.authorizedExecution(Keys.authorization.allowUserManagement, function(){
//execute code, if the loggedin user has rights.
});
Wie ich oben erwähnt habe, ist dies kein Ersatz für die Sicherung des Servers. Dieser Code funktioniert, um eine wunderbare Benutzererfahrung zu bieten.
Autor: Chuck Conway ist ein KI-Ingenieur mit fast 30 Jahren Erfahrung in der Softwareentwicklung. Er entwickelt praktische KI-Systeme – Content-Pipelines, Infrastruktur-Agenten und Tools, die echte Probleme lösen – und teilt seine Erkenntnisse unterwegs. Verbinden Sie sich mit ihm in den sozialen Medien: X (@chuckconway) oder besuchen Sie ihn auf YouTube und auf SubStack.