På et tidspunkt trenger en applikasjon autorisasjon. Dette betyr at ulike tilgangsnivåer oppfører seg forskjellig på et nettsted (eller hva som helst for den saks skyld). Det kan være alt fra å se data til hele områder som ikke er tilgjengelige for en gruppe brukere.
I ikke-Single Page Applications (SPA), er et claim eller en rolle knyttet til data eller et område av applikasjonen, enten har brukeren denne rollen eller claimet eller ikke. I en SPA er det det samme, men med en stor ansvarsfraskrivelse. En SPA lastes ned til nettleseren. På dette punktet har nettleseren total kontroll over koden. En ondsinnet person kan endre koden for å gjøre sitt budskap.
Fordi SPAer ikke kan sikres, er autentisering og autorisasjon i en SPA ganske enkelt brukeropplevelse. All meningsfull sikkerhet må gjøres på webserveren. Denne artikkelen dekker ikke sikring av API-en din mot angrep. Jeg anbefaler å se en video fra Pluralsight eller lese et papir som tar opp sikkerhet for serverteknologien din.
Hensikten med denne artikkelen er å vise deg hvordan jeg la til en autorisasjonsbrukeropplevelse til min Angular 1.x SPA.
Sikkerhetsscopet
Jeg har identifisert 3 områder av brukergrensesnittet som trenger autorisasjon: Elementer (HTML), Ruter, og Data.
Bare en påminnelse, sikring av en SPA er ingen erstatning for sikring av serveren. Tillatelser på klienten er ganske enkelt for å holde ærlige mennesker ærlige og for å gi brukeren en god opplevelse.
De 3 områdene i detalj:
Elementer
Du må skjule spesifikke HTML-elementer. Det kan være en etikett, en tabell med data, en knapp, eller et hvilket som helst element på siden.
Ruter
Du vil skjule hele ruter. I visse tilfeller vil du ikke at brukeren skal få tilgang til en visning. Ved å sikre ruten kan en bruker ikke navigere til visningen. I stedet vil de få vist en “Du er ikke autorisert til å navigere til denne visningen”-melding.
Data
Noen ganger er det ikke nok å skjule elementene i visningen. En oppmerksomme bruker kan ganske enkelt vise kilden og se de skjulte dataene i HTML-kilden eller se dem strømme til nettleseren. Det vi ønsker er at dataene ikke skal hentes i første omgang.
Å legge til sikkerhet er vanskelig. Først prøvde jeg å begrense tilgangen på HTTP API (på klienten). Jeg innså raskt at dette ikke ville fungere. En bruker har kanskje ikke direkte tilgang til dataene, men det betyr ikke at de ikke har indirekte tilgang til dataene. På HTTP API-laget (vanligvis en av de laveste i applikasjonen) kan vi ikke fortelle konteksten for anropet og kan derfor ikke anvende sikkerhetshensyn på det.
Nedenfor har jeg gitt kodingseksempler:
Kode
Jeg opprettet en tjeneste for autorisasjonskontrollkoden. Dette er hjertet av autorisasjonen. Alle autorisasjonsforespørsler bruker denne tjenesten for å sjekke om brukeren er autorisert for den aktuelle handlingen.
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;
}
});
Autorisér direktiv
Autorisér-direktivet kan brukes på ethvert HTML-element som du vil skjule fra brukere uten et spesifikt tilgangsnivå. Hvis brukeren har tilgangstoken som en del av sine claims, har de lov til å se elementet. Hvis de ikke gjør det, er det skjult for dem.
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);
});
}
};
}]);
Elementer
Jeg er avhengig av faner i applikasjonen min. Jeg bruker autorisér-direktivet på fanen som jeg vil skjule fra brukere uten riktige claims.
<tabset>
<tab ng-cloak heading="Users" authorize="{{allowUserManagement}}">
...html content
</tab>
</tabset>
Ruter
Jeg bruker ui-router. Dessverre for de som ikke gjør det, har jeg ikke kode for den ut-av-boksen AngularJS-rutteren.
I $stateChangeStart autentiserer jeg ruten. Dette er koden i den hendelsen.
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
AuthenticationManager.authenticate(event, toState, toParams);
});
Funksjonen som autoriserer ruten. Hvis den er autorisert, får ruten lov til å fortsette. Hvis den ikke er autorisert, vises en melding til brukeren og de blir dirigert til hjemmesiden.
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();
}
}
I denne ruterdefinisjonene vil du legge merke til en egenskap kalt “autorisasjon”. Hvis brukeren har dette claimet, har de lov til å fortsette.
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' }
});
});
Data
I noen tilfeller vil du ikke gjøre en forespørsel til serveren for dataene. Hvis brukeren har claimet, vil de få lov til å gjøre forespørselen.
Autorisér-konteksten ovenfor ved begynnelsen av artikkelen viser koden for authoriedExecution. Her ser du bruken av den.
AuthorizationContext.authorizedExecution(Keys.authorization.allowUserManagement, function(){
//execute code, if the loggedin user has rights.
});
Som jeg nevnte ovenfor, er dette ingen erstatning for sikring av serveren. Denne koden fungerer for å gi en fantastisk brukeropplevelse.
Forfatter: Chuck Conway er en AI-ingeniør med nesten 30 års erfaring innen programvareutvikling. Han bygger praktiske AI-systemer—innholdspipelines, infrastrukturagenter og verktøy som løser virkelige problemer—og deler det han lærer underveis. Koble til ham på sosiale medier: X (@chuckconway) eller besøk ham på YouTube og på SubStack.