web-dev-qa-db-ja.com

ルートガードにパラメーターを渡す

私は、ガードを使用してそれらの役割に基づいてアプリの一部へのnavをブロックする必要がある多くの役割を持つアプリに取り組んでいます。ロールごとに個別のガードクラスを作成できることを理解していますが、何らかの方法でパラメーターを渡すことができる1つのクラスが必要です。言い換えれば、これに似たことができるようになりたいと思います。

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

しかし、渡すのはガードの型名だけなので、それを行う方法は考えられません。弾丸を噛んで、役割ごとに個別のガードクラスを作成し、代わりに単一のパラメーター化された型を持つという優雅さの幻想を打ち砕く必要がありますか?

66
Brian Noyes

これをしなければなりません。

代わりにforRole()を使用して、これを使用する必要があります。

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

roleGuardでこれを使用します

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

    let roles = route.data.roles as Array<string>;
    ...
}
141
Hasan Beheshti

これについての私の見解と、プロバイダーが見つからないという問題の可能な解決策があります。

私の場合、パラメーターとして許可または許可のリストを受け取るガードがありますが、役割を持つのと同じものです。

許可の有無にかかわらず、authガードを処理するクラスがあります。

@Injectable()
export class AuthGuardService implements CanActivate {

    checkUserLoggedIn() { ... }

これは、ユーザーのアクティブなセッションなどのチェックを扱います。

また、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;
  }

これにより、メソッドを使用して、ルーティングモジュールの権限パラメーターに基づいていくつかのカスタムガードを登録できます。

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

forPermissionの興味深い部分はAuthGuardService.guards.Pushです。これにより、基本的にforPermissionsが呼び出されてカスタムガードクラスが取得され、この配列にも格納されます。これはメインクラスでも静的です。

public static guards = [ ]; 

次に、この配列を使用してすべてのガードを登録できます-アプリモジュールがこれらのプロバイダーを登録するまでにルートが定義され、すべてのガードクラスが作成されていることを確認する限り、これは問題ありませんこれらのプロバイダーをリスト内で可能な限り低く保つ-ルーティングモジュールを用意すると役立ちます):

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

お役に立てれば。

5
Ovidiu Dolha

@AluanHaddadのソリューションは、「プロバイダーなし」エラーを提供しています。ここにその修正があります(それは汚い感じがしますが、私はより良いものを作るスキルを欠いています)。

概念的には、roleGuardによって作成された各動的に生成されたクラスをプロバイダーとして登録します。

そのため、チェックされるすべてのロールに対して:

canActivate: [roleGuard('foo')]

あなたが持っている必要があります:

providers: [roleGuard('foo')]

ただし、@ AluanHaddadのソリューションは、roleGuardパラメーターが同じであっても、rolesの呼び出しごとに新しいクラスを生成します。 lodash.memoizeを使用すると、次のようになります。

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

ロールの組み合わせごとに新しいクラスが生成されるため、プロバイダーとして登録する必要がありますeveryロールの組み合わせ。つまりあなたが持っている場合:

canActivate: [roleGuard('foo')]canActivate: [roleGuard('foo', 'bar')]両方を登録する必要があります:providers[roleGuard('foo'), roleGuard('foo', 'bar')]

より良い解決策は、roleGuard内のグローバルプロバイダーコレクションにプロバイダーを自動的に登録することですが、私が言ったように、それを実装するスキルがありません。

0
THX-1138