Estoy trabajando en una aplicación que tiene muchos roles que necesito para usar guardias para bloquear la navegación a partes de la aplicación en función de esos roles. Me doy cuenta de que puedo crear clases de guardia individuales para cada rol, pero preferiría tener una clase a la que de alguna manera podría pasar un parámetro. En otras palabras, me gustaría poder hacer algo similar a esto:
{ path: 'super-user-stuff', component: SuperUserStuffComponent, canActivate: [RoleGuard.forRole('superUser')] }Pero dado que todo lo que pasa es el nombre de tipo de su guardia, no puedo pensar en una manera de hacerlo. ¿Debería simplemente morder la bala y escribir las clases de guardia individuales por rol y romper mi ilusión de elegancia al tener un solo tipo parametrizado en su lugar?
En lugar de usar forRole() , puedes hacer esto:
{ path: 'super-user-stuff', component: SuperUserStuffComponent, canActivate: [RoleGuard], data: {roles: ['SuperAdmin', ...]} }y usa esto en tu RoleGuard
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : Observable<boolean> | Promise<boolean> | boolean { let roles = route.data.roles as Array<string>; ... }Esta es mi opinión sobre esto y una posible solución para el problema del proveedor faltante.
En mi caso tenemos un guardián que toma como parámetro un permiso o lista de permisos, pero es lo mismo tener un rol.
Tenemos una clase para tratar con guardias de autenticación con o sin permiso:
@Injectable() export class AuthGuardService implements CanActivate { checkUserLoggedIn() { ... }Esto se trata de verificar la sesión activa del usuario, etc.
También contiene un método utilizado para obtener una protección de permisos personalizada, que en realidad depende del propio AuthGuardService
static forPermissions(permissions: string | string[]) { @Injectable() class AuthGuardServiceWithPermissions { constructor(private authGuardService: AuthGuardService) { } // uses the parent class instance actually, but could in theory take any other deps canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { // checks typical activation (auth) + custom permissions return this.authGuardService.canActivate(route, state) && this.checkPermissions(); } checkPermissions() { const user = ... // get the current user // checks the given permissions with the current user return user.hasPermissions(permissions); } } AuthGuardService.guards.push(AuthGuardServiceWithPermissions); return AuthGuardServiceWithPermissions; }Esto nos permite usar el método para registrar algunas protecciones personalizadas según el parámetro de permisos en nuestro módulo de enrutamiento:
.... { path: 'something', component: SomeComponent, canActivate: [ AuthGuardService.forPermissions('permission1', 'permission2') ] }, La parte interesante de forPermission es AuthGuardService.guards.push : esto básicamente asegura que cada vez que se llame a forPermissions para obtener una clase de protección personalizada, también la almacenará en esta matriz. Esto también es estático en la clase principal:
public static guards = [ ];Luego, podemos usar esta matriz para registrar todos los guardias; esto está bien siempre que nos aseguremos de que, para cuando el módulo de la aplicación registre estos proveedores, las rutas se hayan definido y se hayan creado todas las clases de guardia (por ejemplo, verifique el orden de importación y mantenga estos proveedores lo más bajo posible en la lista; tener un módulo de enrutamiento ayuda):
providers: [ // ... AuthGuardService, ...AuthGuardService.guards, ]Espero que esto ayude.
Otra opción combinación de enfoque con data y función de fábrica:
export function canActivateForRoles(roles: Role[]) { return {data: {roles}, canActivate: [RoleGuard]} } export class RoleGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) : Observable<boolean> | Promise<boolean> | boolean { const roles = route.data.roles as Role[]; ... } } ... { path: 'admin', component: AdminComponent, ...canActivateWithRoles([Role.Admin]) },