web-dev-qa-db-fra.com

Routage angulaire2 canActivate et AuthGuard (JWT) avec paramètre de rôle d'utilisateur

Dans ce exaple project avec l'authentification JWT, nous voyons comment autoriser uniquement les utilisateurs authentifiés à suivre un itinéraire:

import { RouterConfig } from '@angular/router';
import { Home } from './home';
import { Login } from './login';
import { Signup } from './signup';
import { AuthGuard } from './common/auth.guard';

export const routes: RouterConfig = [
  { path: '',       component:  Login },
  { path: 'login',  component: Login },
  { path: 'signup', component: Signup },
  { path: 'home',   component: Home, canActivate: [AuthGuard] },
  { path: '**',     component: Login },
];

Je voudrais faire un pas en avant et indiquer également quel rôle d'utilisateur a "accès" à la route - mais je ne sais pas comment passer un argument à canActivate AuthGuard (src) . Donc, j'aimerais réaliser quelque chose comme ceci (par exemple, j'ai deux rôles: administrateur et employé):

  { path: 'home',   component: Home, canActivate: [AuthGuard] },
  { path: 'users',   component: AdminUsers, canActivate: [AuthGuard('Admin')] },
  { path: 'users',   component: Employees, canActivate: [AuthGuard('Employee')] },

Où mon AuthGuard pourrait ressembler à quelque chose comme ceci (où userRole (= Admin ou Employee ou null) est passé en paramètre à AuthGuard):

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private router: Router) {}

  canActivate(userRole) {
    if (!userRole || JWT.user().role == userRole) {
      return true;
    }

    this.router.navigate(['/login']);
    return false;
  }
}

où JWT.user.role est un assistant qui lit le rôle d'utilisateur stocké dans le jeton JWT. Existe-t-il un moyen de faire quelque chose de similaire comme ci-dessus?

41
Kamil Kiełczewski

La signature de CanActivate ne vous permettra pas de passer un userRole comme vous le souhaitez. https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/router/src/interfaces.ts#L54

Il est probablement préférable de faire des classes séparées pour chacun de vos cas de rôle d'utilisateur. C’est également le cas dans la documentation officielle: https://angular.io/docs/ts/latest/api/router/index/CanActivate-interface.html

6
cienki

Vous pouvez définir le paramètre data de la route avec le rôle suivant:

const appRoutes: Routes = [
{ 
  path: 'account/super-secure', 
  component: SuperSecureComponent, 
  canActivate: [RoleGuard], 
  data: { roles: ['super-admin', 'admin'] } 
}];

et ensuite ceci dans canActivate de RoleGuard:

canActivate(route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {

    let roles = route.data["roles"] as Array<string>;
    return (roles == null || roles.indexOf("the-logged-user-role") != -1);
}

Je pense que cela pourrait être une autre façon de le faire au lieu de créer une garde pour chaque rôle. En fait, je prendrais cette route car elle nécessite moins de code et gère très bien le problème.

82
Arman

[~ # ~] note [~ # ~] : applicable pour angular-rc.4 <

@ KamilKiełczewski, @NikolayRusev,

  1. ajouté dans la route des données supplémentaires avec un tableau de routes:

...
{
    path: "customers",
    component: CustomersCmp,
    data: { roles: ["admin"] }
},
...

et dans CanActivate, vous pouvez obtenir path à partir du premier paramètre, rechercher le même chemin dans route config et obtenir les rôles décrits à partir des données:

public canActivate(route: ActivatedRouteSnapshot): boolean {
    let path = route._urlSegment.pathsWithParams[0].path;
    let roles;

    if (route._routeConfig.path == path) {
        roles = route._routeConfig.data.roles
    } else {
        roles = route._routeConfig.children.find(_route => _route.path == path).data.roles;
    }

    if (...) {
        return true;
    }

    return false;
}

Bien sûr, il vaudrait mieux éviter les propriétés privées, et vous le pouvez, mais je ne me souviens pas comment je l’ai fait exactement.

Mais pour mes buts je l'ai refait de manière différente. L'énorme inconvénient de cette approche, en ce qui concerne la protection basée sur les rôles, est que chaque utilisateur ayant des rôles différents peut voir tous les itinéraires si vous les rendez dans un composant automatiquement, pas manuellement.

  1. vous pouvez prolonger router-outlet
2
Andrei Shostik