경로 가드에 매개 변수 전달


103

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

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

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

답변:


220

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

훌륭한 옵션도 있습니다. 감사합니다. 나는 Aluan의 공장 방법 접근 방식을 조금 더 좋아하지만 가능성에 대한 내 두뇌를 확장 해 주셔서 감사합니다!
— Brian Noyes

7
이 데이터의 보안 성은 무관하다고 생각합니다. 서버 측에서 인증 및 권한 부여를 사용해야합니다. 가드의 요점은 애플리케이션을 완전히 보호하는 것이 아니라고 생각합니다. 누군가 그것을 "해킹"하고 관리자 페이지로 이동하면 서버에서 보안 데이터를 얻지 못합니다. 제 생각에는 괜찮은 관리자 구성 요소 만 볼 수 있습니다. 나는 이것이 받아 들여진 것보다 훨씬 더 나은 해결책이라고 생각합니다. 허용되는 솔루션은 종속성 주입을 중단합니다.
— bucicimaci

1
이것은 좋은 솔루션이며 내 일반 AuthGuard에서 훌륭하게 작동합니다.
— SAV

3
이 솔루션은 훌륭하게 작동합니다. 내 문제는 간접적 인 계층에 의존한다는 것입니다. 이 코드를 보는 사람이 코드가 roles미리 작동하는 방식을 알지 못한 채 객체와 경로 가드가 연결되어 있음을 알 수있는 방법은 없습니다. Angular가 더 선언적인 방식으로이 작업을 수행하는 방법을 지원하지 않는다는 것은 짜증납니다. (이것이 완벽하게 합리적인 해결책이 아니라 Angular를
— 탄식하는

1
@KhalilRavanna 감사합니다, 예,하지만이 솔루션을 여러 번 사용했고 다른 솔루션으로 옮겼습니다. 내 새 솔루션은 하나의 RoleGaurd와 Map <URL, AccessRoles> 상수가있는 "access.ts"이름을 가진 파일 하나입니다. 그런 다음 RoleGaurd에서 사용합니다. 앱에서 액세스를 제어하려는 경우 특히 하나의 프로젝트에 둘 이상의 앱이있을 때이 새로운 방법이 훨씬 더 낫다고 생각합니다.
— Hasan Beheshti

11

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

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

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

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

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


1
이 솔루션은 정적 오류를 제공합니다. ERROR in Error가 기호 값을 정적으로 해결하는 중 발생했습니다.
— Arninja

이 솔루션은 개발 용으로 저에게 ERROR in Error during template compile of 'RoutingModule' Function calls are not supported in decorators but 'PermGuardService' was called.
— 효과적이지만

자체 라우팅 모듈이있는 지연로드 된 모듈에서 작동합니까?
— crush

2

@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것이지만 제가 말했듯이이를 구현할 기술이 부족합니다.


이 기능적인 접근 방식을 정말 좋아하지만 DI (클래스)와 함께 클로저를 혼합하면 오버 헤드처럼 보입니다.
— BILL
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.