it-swarm.com.de

Parameter in den Routenwächter übergeben

Ich arbeite an einer App, die über viele Rollen verfügt, die ich zum Schutz von Navis für Teile der App verwenden muss, die auf diesen Rollen basieren. Mir ist klar, dass ich für jede Rolle individuelle Guard-Klassen erstellen kann, aber ich hätte lieber eine Klasse, an die ich irgendwie einen Parameter übergeben könnte. Mit anderen Worten, ich würde gerne etwas Ähnliches machen können:

{ 
  path: 'super-user-stuff', 
  component: SuperUserStuffComponent,
  canActivate: [RoleGuard.forRole('superUser')]
}

Da Sie jedoch nur den Typnamen Ihrer Wache bestanden haben, können Sie sich keinen Weg vorstellen, dies zu tun. Soll ich einfach die Kugel abbeißen und die einzelnen Guard-Klassen pro Rolle schreiben und meine Illusion von Eleganz zerstören, wenn ich stattdessen einen einzigen parametrisierten Typ habe?

51
Brian Noyes

Du musst das tun.

verwenden Sie stattdessen forRole(), sollten Sie Folgendes verwenden: 

{ 
 path: 'super-user-stuff', 
 component: SuperUserStuffComponent,
 canActivate: RoleGuard,
 data: {roles: ['SuperAdmin', ...]}
}

und verwenden Sie dies in Ihrem RoleGuard

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
    : Observable<boolean> | Promise<boolean> | boolean  {

    let roles = route.data["roles"] as Array<string>;
    ...
}
103
Hasan Beheshti

Hier ist meine Meinung dazu und eine mögliche Lösung für das fehlende Providerproblem.

In meinem Fall haben wir einen Wächter, der eine Erlaubnis oder eine Liste von Berechtigungen als Parameter annimmt, aber es ist dasselbe, was eine Rolle hat.

Wir haben eine Klasse für den Umgang mit Auth Guards mit oder ohne Erlaubnis:

@Injectable()
export class AuthGuardService implements CanActivate {

    checkUserLoggedIn() { ... }

Dies betrifft die Überprüfung der aktiven Sitzung des Benutzers usw.

Es enthält auch eine Methode zum Abrufen eines benutzerdefinierten Berechtigungsschutzes, die eigentlich von der Variablen AuthGuardService abhängig ist

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;
  }

Dadurch können wir die Methode verwenden, um einige benutzerdefinierte Wachen basierend auf den Berechtigungsparametern in unserem Routingmodul zu registrieren:

....
{ path: 'something', 
  component: SomeComponent, 
  canActivate: [ AuthGuardService.forPermissions('permission1', 'permission2') ] },

Der interessante Teil von forPermission ist AuthGuardService.guards.Push - dies stellt im Grunde sicher, dass jedes Mal, wenn forPermissions aufgerufen wird, um eine benutzerdefinierte Guard-Klasse zu erhalten, diese ebenfalls in diesem Array gespeichert wird. Dies ist auch für die Hauptklasse statisch:

public static guards = [ ]; 

Dann können wir dieses Array verwenden, um alle Wachen zu registrieren. Dies ist in Ordnung, solange sichergestellt ist, dass bis zum Zeitpunkt der Registrierung dieser Anbieter durch das App-Modul die Routen definiert und alle Wächterklassen erstellt wurden (z. B. Importreihenfolge prüfen und Halten Sie diese Anbieter so niedrig wie möglich in der Liste - ein Routing-Modul hilft dabei).

providers: [
    // ...
    AuthGuardService,
    ...AuthGuardService.guards,
]

Hoffe das hilft.

4
Ovidiu Dolha

Die Lösung von @ AluanHaddad gibt den Fehler "Kein Anbieter" aus. Hier ist ein Fix dafür (es fühlt sich schmutzig an, aber mir fehlen die Fähigkeiten, um eine bessere zu machen).

Konzeptionell registriere ich als Anbieter jede dynamisch generierte Klasse, die von roleGuard erstellt wurde.

Also für jede Rolle geprüft:

canActivate: [roleGuard('foo')]

du solltest haben:

providers: [roleGuard('foo')]

Die Lösung von @ AluanHaddad im aktuellen Zustand generiert jedoch für jeden Aufruf von roleGuard eine neue Klasse, selbst wenn der Parameter roles gleich ist. Mit lodash.memoize sieht es so aus:

export var roleGuard = _.memoize(function forRole(...roles: string[]): Type<CanActivate> {
    return class AuthGuard implements CanActivate {
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
            Observable<boolean>
            | Promise<boolean>
            | boolean {
            console.log(`checking access for ${roles.join(', ')}.`);
            return true;
        }
    }
});

Beachten Sie, dass jede Kombination von Rollen eine neue Klasse generiert. Sie müssen sich also als Anbieter jede Kombination von Rollen registrieren. Das heißt Wenn Sie haben:

canActivate: [roleGuard('foo')] und canActivate: [roleGuard('foo', 'bar')] müssen Sie beide registrieren: providers[roleGuard('foo'), roleGuard('foo', 'bar')]

Eine bessere Lösung wäre, Provider automatisch in einer globalen Provider-Sammlung in roleGuard zu registrieren, aber wie gesagt, mir fehlen die Fähigkeiten, um dies zu implementieren.

0
THX-1138