programing

여러 개를 사용할 수 있음처음 실패할 때 가드가 모두 실행됨

cafebook 2023. 8. 1. 20:47
반응형

여러 개를 사용할 수 있음처음 실패할 때 가드가 모두 실행됨

두 개가 있는 노선이 있습니다.canActivateAuthGuard그리고.RoleGuard . 첫 (. . . . . . . .AuthGuard 로그인하지 않은 로 리디렉션합니다.에서 사용자가 로그인했는지 확인하고 로그인 페이지로 리디렉션합니다.두 번째는 사용자에게 페이지를 볼 수 있는 역할이 정의되어 있는지 확인하고, 정의되지 않은 경우 권한이 없는 페이지로 리디렉션합니다.

canActivate: [ AuthGuard, RoleGuard ]
...
export class AuthGuard implements CanActivate {
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
        ...
        this.router.navigate(['/login']);
        resolve(false);
}

export class RoleGuard implements CanActivate {
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
        ...
        this.router.navigate(['/unauthorized']);
        resolve(false);
}

문제는 내가 그 경로에 접속하고 로그인하지 않을 때, 나는 그것을 누릅니다.AuthGuard 오류는 에 "" "" " " " " "로 합니다./login하지만, 비록.AuthGuardRoleGuard에는 " 는하대실이다로동합다니으음며원"로 이동합니다./unauthorized.

제 생각에는 첫 번째 가드가 실패하면 다음 가드를 실행하는 것은 무의미합니다.이 행동을 강제할 방법이 있습니까?

최신 Angular 버전에서는 두 가드가 모두 돌아오는 경우에도false둘 다 여전히 실행될 것입니다.

그러나 예제를 사용하여 이 문제를 해결할 수 있는 방법은 다음과 같습니다.RoleGuard "URL"이 "URL"인 Role로그인해야 역할을 할 수 있기 때문에 필요합니다.에▁your다▁change니▁in있습▁can수▁you를 변경할 수 있습니다.RoleGuard대상:

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(private _authGuard: AuthGuard) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
    return this._authGuard.canActivate(route, state).then((auth: boolean) => {
      if(!auth) {
        return false;
      }
      //... your role guard check code goes here
    });
  }
}

@ Duc @Pierre Duc @Pierre Duc ▁@▁▁as이pier와바급.dataRoute마스터 가드와 함께 클래스를 사용하여 이 문제를 해결할 수 있습니다.

문제

우선 각진은 경비원을 동시에 부르는 기능을 지원하지 않습니다.따라서 첫 번째 가드가 비동기식이고 아약스 호출을 시도하는 경우, 가드 1에서 아약스 요청이 완료되기도 전에 나머지 가드가 모두 해고됩니다.

저도 비슷한 문제에 직면했고 이렇게 해결했습니다.


해결책

마스터 가드를 만들고 마스터 가드가 다른 가드의 실행을 처리하도록 하는 것입니다.

이 경우 라우팅 구성에는 마스터 가드가 유일한 가드로 포함됩니다.

가드가 경로에 알 수 마터스가특경정대트가될드리대다추다알니가음합을을 추가합니다.dataRoute.

data속성은 경로에 데이터를 첨부할 수 있는 키 값 쌍입니다.

그런 다음 다음 다음을 사용하여 가드에서 데이터에 액세스할 수 있습니다.ActivatedRouteSnapshot의 매 변 수 개의 파라미터.canActivate근위대의 방법

이 솔루션은 복잡해 보이지만 응용프로그램에 통합되면 가드가 제대로 작동할 수 있습니다.

다음 예에서는 이 접근 방식을 설명합니다.


모든 응용 프로그램 가드를 매핑하는 상수 개체 -

export const GUARDS = {
    GUARD1: "GUARD1",
    GUARD2: "GUARD2",
    GUARD3: "GUARD3",
    GUARD4: "GUARD4",
}

응용 프로그램 가드 -

import { Injectable } from "@angular/core";
import { Guard4DependencyService } from "./guard4dependency";

@Injectable()
export class Guard4 implements CanActivate {
    //A  guard with dependency
    constructor(private _Guard4DependencyService:  Guard4DependencyService) {}

    canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
        return new Promise((resolve: Function, reject: Function) => {
            //logic of guard 4 here
            if (this._Guard4DependencyService.valid()) {
                resolve(true);
            } else {
                reject(false);
            }
        });
    }
}

라우팅 구성 -

import { Route } from "@angular/router";
import { View1Component } from "./view1";
import { View2Component } from "./view2";
import { MasterGuard, GUARDS } from "./master-guard";
export const routes: Route[] = [
    {
        path: "view1",
        component: View1Component,
        //attach master guard here
        canActivate: [MasterGuard],
        //this is the data object which will be used by 
        //masteer guard to execute guard1 and guard 2
        data: {
            guards: [
                GUARDS.GUARD1,
                GUARDS.GUARD2
            ]
        }
    },
    {
        path: "view2",
        component: View2Component,
        //attach master guard here
        canActivate: [MasterGuard],
        //this is the data object which will be used by 
        //masteer guard to execute guard1, guard 2, guard 3 & guard 4
        data: {
            guards: [
                GUARDS.GUARD1,
                GUARDS.GUARD2,
                GUARDS.GUARD3,
                GUARDS.GUARD4
            ]
        }
    }
];

마스터 가드 -

import { Injectable } from "@angular/core";
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router";

//import all the guards in the application
import { Guard1 } from "./guard1";
import { Guard2 } from "./guard2";
import { Guard3 } from "./guard3";
import { Guard4 } from "./guard4";

import { Guard4DependencyService } from "./guard4dependency";

@Injectable()
export class MasterGuard implements CanActivate {

    //you may need to include dependencies of individual guards if specified in guard constructor
    constructor(private _Guard4DependencyService:  Guard4DependencyService) {}

    private route: ActivatedRouteSnapshot;
    private state: RouterStateSnapshot;

    //This method gets triggered when the route is hit
    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {

        this.route = route;
        this.state = state;

        if (!route.data) {
            Promise.resolve(true);
            return;
        }

        //this.route.data.guards is an array of strings set in routing configuration

        if (!this.route.data.guards || !this.route.data.guards.length) {
            Promise.resolve(true);
            return;
        }
        return this.executeGuards();
    }

    //Execute the guards sent in the route data 
    private executeGuards(guardIndex: number = 0): Promise<boolean> {
        return this.activateGuard(this.route.data.guards[guardIndex])
            .then(() => {
                if (guardIndex < this.route.data.guards.length - 1) {
                    return this.executeGuards(guardIndex + 1);
                } else {
                    return Promise.resolve(true);
                }
            })
            .catch(() => {
                return Promise.reject(false);
            });
    }

    //Create an instance of the guard and fire canActivate method returning a promise
    private activateGuard(guardKey: string): Promise<boolean> {

        let guard: Guard1 | Guard2 | Guard3 | Guard4;

        switch (guardKey) {
            case GUARDS.GUARD1:
                guard = new Guard1();
                break;
            case GUARDS.GUARD2:
                guard = new Guard2();
                break;
            case GUARDS.GUARD3:
                guard = new Guard3();
                break;
            case GUARDS.GUARD4:
                guard = new Guard4(this._Guard4DependencyService);
                break;
            default:
                break;
        }
        return guard.canActivate(this.route, this.state);
    }
}

과제들

이 접근 방식의 과제 중 하나는 기존 라우팅 모델의 리팩터링입니다.그러나 변경 사항이 깨지지 않아 부분적으로 수행할 수 있습니다.

이것이 도움이 되길 바랍니다.

Angular 8에서 저는 이것을 할 수 있습니다.이 솔루션은 @planet_hunter의 답변에서 영감을 얻었지만 코드 수가 적었고 이 프로젝트의 요구 사항이었던 무거운 리프팅에 대해 관찰 가능한 항목을 사용했습니다.

원하는 이름으로 모든 가드를 순서대로 실행할 수 있는 가드를 만듭니다.

@Injectable({
    providedIn: 'root'
})
export class SyncGuardHelper implements CanActivate {
    public constructor(public injector: Injector) {
    }
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
        return from(route.data.syncGuards).pipe(concatMap((value) => {
            const guard = this.injector.get(value);
            const result = guard.canActivate(route, state);
            if (result instanceof Observable) {
                return result;
            } else if (result instanceof Promise) {
                return from(result);
            } else {
                return of(result);
            }
        }), first((x) => x === false || x instanceof UrlTree, true));
    }
}

경로 파일에서 데이터 속성을 사용하여 실행할 가드를 순서대로(동기적으로) 추가합니다.

const routes: Routes = [
    {
        path: '',
        component: MyComponent,
        canActivate: [SyncGuardHelper],
        data: {
            syncGuards: [
                Guard1,
                Guard2,
                Guard3
            ]
        }
    },
    // other routes
]

제가 오늘 이 해결책을 생각해내야 했기 때문에 피드백이 있으면 댓글로 남겨주시면 이 답변을 개선할 수 있습니다.

이 문제는 Angular 7.1 이상에서 해결되었습니다.

경비원들은 이제 우선권을 가지고 있습니다.
작동 방식에 대한 자세한 설명은 이 훌륭한 블로그 게시물에서 확인할 수 있습니다.

블로그 게시물에서 다음 예를 인용합니다.

canActivate: [CanActivateRouteGuard, CanActivateRouteGuard2], 

다음과 같이 작동합니다.

어진모든경에 .canActivate어레이는 병렬로 실행되지만 라우터는 우선 순위가 높은 가드가 완료될 때까지 대기한 후 계속 진행합니다.위의 예에서는 다음과 같습니다.

  • 라 할지라도CanActivateRouteGuard2를 반환합니다.UrlTree즉시:
    라우터는 여전히 기다릴 것입니다.CanActivateRouteGuard새 탐색을 시작하기 전에 확인합니다.
  • 한다면CanActivateRouteGuard를 반환합니다.UrlTree:
    그것이 이길 것입니다.
  • 만약 그것이 다시 돌아온다면,false:
    전체 탐색이 실패하고 리디렉션이 발생하지 않습니다.
  • 만약 그것이 단순히 돌아온다면,true:
    다음에 그음에다.UrlTree 된환반이 했습니다.CanActivateRouteGuard2으로 이동합니다.

현재 여러 개의 비동기 가드(약속 반환 또는 관찰 가능)가 동시에 실행됩니다.는 이것에 대한 이슈를 열었습니다: https://github.com/angular/angular/issues/21702 .

위에서 설명한 솔루션의 또 다른 해결 방법은 중첩된 경로를 사용하는 것입니다.

{
  path: '',
  canActivate: [
    AuthGuard,
  ],
  children: [
    {
      path: '',
      canActivate: [
        RoleGuard,
      ],
      component: YourComponent
      // or redirectTo
      // or children
      // or loadChildren
    }
  ]
}

저는 인터넷에서 더 나은 해결책을 찾지 못했지만, 최선의 답변을 안내하기 위해 Rxjs mergeMap을 사용하여 연결된 두 요청을 모두 포함하여 하나의 가드만 사용하기로 결정했습니다. 이는 동일한 엔드포인트에 대한 중복된 통화를 방지하기 위한 것입니다.여기서는 console.log를 피하려면 먼저 트리거된 항목을 확인하기 위해 사용했습니다.

1 getCASUsername은 사용자를 인증하기 위해 호출됩니다(여기서는 볼 수 없는 console.log(1)).
2 사용자 이름이 있습니다.
3 응답을 사용하여 첫 번째 요청 이후에 트리거될 두 번째 요청을 수행합니다(참).
4 반환된 userName을(를) 사용하여 해당 사용자의 역할을 가져옵니다.

이를 통해 통화 시퀀스와 중복 통화 방지를 위한 솔루션을 확보할 수 있습니다.당신에게 도움이 될 수도 있습니다.

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private AuthService  : AuthService,
              private AepApiService: AepApiService) {}

  canActivate(): Observable<boolean> {
    return this.AepApiService.getCASUsername(this.AuthService.token)
      .map(res => {
        console.log(2, 'userName');
        if (res.name) {
          this.AuthService.authenticateUser(res.name);
          return true
        }
      })
      .mergeMap( (res) => {
        console.log(3, 'authenticated: ' + res);
        if (res) {
          return this.AepApiService.getAuthorityRoles(this.AuthService.$userName)
            .map( res => {
              console.log(4, 'roles');
              const roles = res.roles;

              this.AuthService.$userRoles = roles;

              if (!roles.length) this.AuthService.goToAccessDenied();

              return true;
            })
            .catch(() => {
              return Observable.of(false);
            });
        } else {
          return Observable.of(false);
        }
      })
      .catch(():Observable<boolean> => {
        this.AuthService.goToCASLoginPage();
        return Observable.of(false);
      });
  }
}

언급URL : https://stackoverflow.com/questions/40589878/multiple-canactivate-guards-all-run-when-first-fails

반응형