경로 가드에 매개 변수 전달


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.