Author Image

Ruben Smeets

Developer

Stil maar doeltreffend op het gebied van Laravel, Angular en Ionic. Bouwt de mooiste dingen op de beats van Partyraiser. Heeft eerste schreden op het schrijverspad gezet, iets dat goed bevalt.

 

Blog Header

Angular routes authenticatie op basis van rollen en permissies

Op zoek naar hoe je bepaalde routes kan afschermen in Angular voor geauthenticeerde gebruikers? Maar niets gevonden dat aansluit bij jouw probleem? Wij stuitten op hetzelfde probleem en hebben daarom zelf een oplossing bedacht. Natuurlijk delen we de code met je.

Routes afschermen

Recentelijk hebben we een project ontwikkeld waarbij we werken met gebruikers die verschillende rollen en de daarbij horende bevoegdheden hebben. Sommige gebruikers mogen niet bij specifieke informatie of mogen geen bepaalde handelingen verrichten. Anderen alleen tot een zeker level. Wij zochten op de vraag; hoe kunnen we routes afschermen voor gebruikers met verschillende rechten?

Op internet zijn diverse deel oplossingen te vinden maar niet als een algehele oplossing. Daarom hebben we zelf een oplossing bedacht.

Oplossing voor de klant

In deze blog laten we alleen de oplossing aan de client side zien. De backend is gebouwd met Laravel en wordt niet uitgebreid besproken. De backend kent rollen en permissies die gekoppeld kunnen worden aan gebruikers. Deze kunnen aan de client side worden toegewezen. De backend retourneert een object terug met alle benodigde data wanneer de client side hierom vraagt.

Vier stappen

De volgende vier onderdelen worden besproken :

1. LoginGuard: Controleert of de gebruiker is ingelogd.
2. PermissionService: Controleert of de gebruiker de juiste permissie heeft
3. PermissionGuard: Controleert of de gebruiker de juiste permissie heeft voor het benaderen van een betreffende url
4. App-routing module: Elke route bevat een permissie waarmee de gebruiker naar de betreffende url mag navigeren. 

Stap 1- Login Guard

Omdat ons systeem alleen door ingelogde gebruikers mag worden benaderd, gebruiken we een guard. Deze guard wordt elke keer aangeroepen wanneer een bezoeker van het systeem naar een url wil navigeren. In het routing bestand wordt gespecificeerd wanneer de Login guard aangeroepen moet worden.

// CanActivate for Login guard on the highest route.
canActivate: [LoginGuard]

// CanActivate function in login.guard.ts

De canActivate wordt gezet op het hoogste mogelijk route. Bij alle subroutes is deze canActivate nu ook van toepassing.

In de Login guard is de canActivate methode uitgeschreven.

--
    canActivate(next: ActivatedRouteSnapshot,
                state: RouterStateSnapshot): Observable<boolean> {
        return this.authService.isLoggedIn();
    }


--

// CanActivate function in login.guard.ts

Zoals je ziet controleert de LoginGuard bij de authentication service of de gebruiker is ingelogd. Hiervoor wordt een eenvoudige request naar de backend gemaakt om te vragen of de huidige gebruiker is ingelogd. De backend geeft een boolean waarde terug op deze vraag.

Gard voor meerdere routes
In het routing bestand ( Stap 4) wordt bij elke route afzonderlijk aangeven of de guard geactiveerd dient te worden voordat de gebruiker naar een url wil navigeren. Aangezien ons systeem alleen toegankelijk is voor ingelogde gebruikers, passen wij de guard toe op een grotere groep routes. We specificeren de guard voor een aantal routes tegelijkertijd in plaats van de guard bij elke route te specificeren.

Stap 2 - PermissionService

Het systeem kan controleren of de gebruiker is ingelogd, echter is dit nog niet genoeg. We willen middels verschillende permissies bepaalde routes toegankelijk maken voor bepaalde gebruikers.

Wanneer de backend de permissies van de ingelogde gebruiker naar de cliƫnt sides stuurt, zal de PermissionService de permissies uitlezen en controleren of de opgevraagde url benaderd mag worden door de gebruiker. Bij elke url in ons routing bestand wordt de vereiste permissie aangeven.

--

hasPermission(component): Observable<boolean> {
        return this.authService.isLoggedIn().map(res => {
  // Get permissions from user object.
            this.permissions = this.getPermissions();
  // Check if user object has permissions to access the current component.
            return this.checkPermission(component.data.permission);
        });
    }

--

// HasPermisson function in permission.service.ts

Stap 3 - PermissionGuard

Net zoals bij de LoginGuard, wordt ook de PermissionGuard aangeroepen wanneer de ingelogde gebruiker naar een url wil navigeren. De guard maakt gebruik van de PermissionService om te controleren of de gebruiker de juiste permissies heeft.

--

canActivate(next: ActivatedRouteSnapshot,
                state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        return this.permissionService.hasPermission(next);
    }

--

// CanActivate function in permission.guard.ts

Stap 4 - Routing Module

Bij elke url wordt gespecificeerd welke permissie nodig is om naar de url te mogen navigeren. Daarbij wordt ook de PermissionGuard gespecificeerd door bij elke url het `canActivate` te aan te roepen met de PermissionGuard guard.

De permissie wordt mee gegeven middels de optionele data parameter. In deze optionele parameter kan een object met key en value worden gespecificeerd. Middels de ActivatedRouteSnapshot (uit Angular's framework), kan de PermissionGuard deze optionele data uitlezen. In plaats van de data uit te lezen in de guard, wordt deze doorgeven aan de Permission Service. De service heeft nu ook de mogelijkheid om de optionele data te benaderen en zo te controleren of de gebruiker over de juiste permissie beschikt.

--

{
                path: 'shipment',
                children: [
                    {
                        path: '',
                        component: ShipmentListComponent,
                        canActivate: [PermissionGuard],
    // Permission which is need to access this component. 
    // Permission checked by Permission Guard
                        data: {permission: 'list-shipment'}
                    },
                    {
                        path: 'new',
                        component: ShipmentEditComponent,
                        canActivate: [PermissionGuard],
// Permission which is need to access this component. 
    // Permission checked by Permission Guard
                        data: {permission: 'add-shipment'}
                    },

.
.
.
  // CanActivate for Login guard on the highest route.
canActivate: [LoginGuard]

--

// Example routes with guards in app-routing.module.ts

Natuurlijk is de implementatie afhankelijk van jouw systeem en de functionaliteit die je nodig hebt. Heb je een vraag of een betere oplossing? Laat het ons weten.

Lees meer