[angular] 경로 가드에 매개 변수 전달

저는 이러한 역할을 기반으로 앱의 일부에 대한 탐색을 차단하기 위해 가드를 사용해야하는 많은 역할이있는 앱을 작업하고 있습니다. 각 역할에 대해 개별 가드 클래스를 만들 수 있지만 어떻게 든 매개 변수를 전달할 수있는 하나의 클래스를 갖고 싶습니다. 즉, 다음과 유사한 작업을 수행하고 싶습니다.

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

그러나 당신이 통과하는 것은 경비원의 유형 이름이기 때문에 그렇게 할 방법을 생각할 수 없습니다. 총알을 깨물고 역할별로 개별 가드 클래스를 작성하고 대신 단일 매개 변수화 된 유형을 갖는 것에 대한 내 우아함을 깨뜨려야할까요?



답변

을 사용하는 대신 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>;
    ...
}


답변

여기에 내 생각과 누락 된 공급자 문제에 대한 가능한 해결책이 있습니다.

제 경우에는 권한 또는 권한 목록을 매개 변수로 취하는 가드가 있지만 역할이있는 것도 마찬가지입니다.

허가 유무에 관계없이 인증 가드를 다루는 클래스가 있습니다.

@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,
]

도움이 되었기를 바랍니다.


답변

@AluanHaddad의 솔루션은 “제공자 없음”오류를 제공합니다. 여기에 대한 해결책이 있습니다 (더러워진 것 같지만 더 나은 것을 만드는 기술이 부족합니다).

개념적으로는 .NET에서 생성 된 동적으로 생성 된 각 클래스를 공급자로 등록합니다 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;
        }
    }
});

각 역할 조합은 새 클래스를 생성하므로 모든 역할 조합을 공급자로 등록해야 합니다. 즉, 다음이있는 경우 :

canActivate: [roleGuard('foo')]그리고 canActivate: [roleGuard('foo', 'bar')]당신은 모두를 등록해야합니다 :providers[roleGuard('foo'), roleGuard('foo', 'bar')]

더 나은 솔루션은 내부의 글로벌 공급자 컬렉션에 공급자를 자동으로 등록하는 roleGuard것이지만 제가 말했듯이이를 구현할 기술이 부족합니다.


답변