Angular2 canActivate () 호출 비동기 함수


82

Angular2 라우터 가드를 사용하여 내 앱의 일부 페이지에 대한 액세스를 제한하려고합니다. Firebase 인증을 사용하고 있습니다. 사용자가 중포 기지로 로그인되어 있는지 확인하기 위해, 나는 호출이 .subscribe()FirebaseAuth콜백와 객체입니다. 이것은 경비원의 코드입니다.

import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFireAuth } from "angularfire2/angularfire2";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private auth: AngularFireAuth, private router: Router) {}

    canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
        this.auth.subscribe((auth) => {
            if (auth) {
                console.log('authenticated');
                return true;
            }
            console.log('not authenticated');
            this.router.navigateByUrl('/login');
            return false;
        });
    }
}

가드가있는 페이지로 이동하면 authenticated또는 not authenticated콘솔에 인쇄됩니다 (Firebase의 응답을 기다리는 데 약간의 지연 후). 그러나 탐색은 완료되지 않습니다. 또한 로그인하지 않은 경우 /login경로로 리디렉션됩니다 . 따라서 내가 가지고있는 문제 return true는 요청한 페이지를 사용자에게 표시하지 않는다는 것입니다. 나는 이것이 콜백을 사용하고 있기 때문이라고 가정하고 있지만 그렇지 않으면 어떻게하는지 알 수 없습니다. 이견있는 사람?


import Observable 다음과 같이-> import {Observable} from 'rxjs / Observable';
Carlos Pliego

답변:


124

canActivateObservable완료 되는를 반환해야합니다 .

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private auth: AngularFireAuth, private router: Router) {}

    canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
        return this.auth.map((auth) => {
            if (auth) {
                console.log('authenticated');
                return true;
            }
            console.log('not authenticated');
            this.router.navigateByUrl('/login');
            return false;
        }).first(); // this might not be necessary - ensure `first` is imported if you use it
    }
}

있습니다 return실종 내가 사용하는 map()대신 subscribe()때문에 subscribe()반환 Subscription이 아닌를Observable


다른 컴포넌트에서이 클래스를 사용하는 방법을 보여줄 수 있습니까?

당신이 무슨 뜻인지 확실하지. 구성 요소가 아닌 경로와 함께 사용합니다. 참조 angular.io/docs/ts/latest/guide/router.html#!#guards
귄터 Zöchbauer

내 경우에는 Observable이 실행되지 않습니다. 콘솔 출력이 보이지 않습니다. 그러나 조건부로 부울을 반환하면 (문서에서와 같이) 콘솔이 기록됩니다. this.auth는 간단한 Observable입니까?
cortopy

@cortopy auth는 옵저버 블 에서 내 보낸 값입니다 ( true또는 false). 옵저버 블은 라우터가 구독 할 때 실행됩니다. 구성에 누락 된 것이있을 수 있습니다.
Günter Zöchbauer

1
@ günter-zöchbauer 예, 감사합니다. 구독자를 구독하고 있다는 사실을 몰랐습니다. 답변 해 주셔서 감사합니다! 그것은 훌륭하게 작동합니다
cortopy

27

Observable비동기 논리 부분을 처리하는 데 사용할 수 있습니다 . 예를 들어 테스트하는 코드는 다음과 같습니다.

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { DetailService } from './detail.service';

@Injectable()
export class DetailGuard implements CanActivate {

  constructor(
    private detailService: DetailService
  ) {}

  public canActivate(): boolean|Observable<boolean> {
    if (this.detailService.tempData) {
      return true;
    } else {
      console.log('loading...');
      return new Observable<boolean>((observer) => {
        setTimeout(() => {
          console.log('done!');
          this.detailService.tempData = [1, 2, 3];
          observer.next(true);
          observer.complete();
        }, 1000 * 5);
      });
    }
  }
}

2
그것은 실제로 저를 정말로 도왔던 좋은 대답입니다. 비슷한 질문이 있었지만 수락 된 답변으로 문제가 해결되지 않았습니다. 이 사람은 한
콘스탄틴

사실 정답입니다 !!! 비동기 함수를 호출하는 canActivate 메서드를 사용하는 좋은 방법입니다.
다닐

18

canActivate너무 Promise해결하는 반환 할 수 boolean있습니다


13

약속으로 true | false를 반환 할 수 있습니다.

import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthService} from "../services/authorization.service";

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

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  return new Promise((resolve, reject) => {
  this.authService.getAccessRights().then((response) => {
    let result = <any>response;
    let url = state.url.substr(1,state.url.length);
    if(url == 'getDepartment'){
      if(result.getDepartment){
        resolve(true);
      } else {
        this.router.navigate(['login']);
        resolve(false);
      }
    }

     })
   })
  }
}

1
그 새로운 Promise 객체는 저를 구합니다. : D 감사합니다.
canmustu

감사합니다. 이 솔루션은 api 호출이 응답 할 때까지 기다린 다음 리디렉션합니다.
Philip Enc

이것은 명시 적 Promise 생성자 반 패턴 ( stackoverflow.com/questions/23803743/… ) 의 예처럼 보입니다 . 코드 예제에서는 getAccessRights ()가 이미 Promise를 반환한다고 제안하므로 직접 반환 return this.authService.getAccessRights().then...하고을 래핑하지 않고 부울 결과를 반환 하려고합니다 resolve.
rob3c

6

가장 인기있는 답변을 확장합니다. AngularFire2 용 Auth API가 약간 변경되었습니다. AngularFire2 AuthGuard를 달성하기위한 새로운 서명입니다.

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable()
export class AuthGuardService implements CanActivate {

  constructor(
    private auth: AngularFireAuth,
    private router : Router
  ) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean>|boolean {
    return this.auth.authState.map(User => {
      return (User) ? true : false;
    });
  }
}

참고 : 이것은 매우 순진한 테스트입니다. User 인스턴스를 콘솔 로그하여 사용자의 더 자세한 측면에 대해 테스트 할 것인지 확인할 수 있습니다. 그러나 적어도 로그인하지 않은 사용자로부터 경로를 보호해야합니다.


5

최신 버전의 AngularFire에서 다음 코드가 작동합니다 (최상 답변 관련). "파이프"방법의 사용법에 유의하십시오.

import { Injectable } from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {AngularFireAuth} from '@angular/fire/auth';
import {map} from 'rxjs/operators';
import {Observable} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthGuardService implements CanActivate {

  constructor(private afAuth: AngularFireAuth, private router: Router) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.afAuth.authState.pipe(
      map(user => {
        if(user) {
          return true;
        } else {
          this.router.navigate(['/login']);
          return false;
        }
      })
    );
  }
}


isLoggedIn () 이후에 XHR 호출이 1 개 더 있고 XHR 결과가 두 번째 XHR 호출에 사용됩니다. 첫 번째 결과를 수락하는 두 번째 ajax 호출을받는 방법은 무엇입니까? 당신이 준 예는 매우 쉽습니다. 다른 ajax도 가지고 있다면지도 사용법을 알려 주실 수 있습니까?
Pratik

2

제 경우에는 응답 상태 오류에 따라 다른 동작을 처리해야했습니다. 이것이 RxJS 6+에서 작동하는 방식입니다.

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private auth: AngularFireAuth, private router: Router) {}

  public canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {
    return this.auth.pipe(
      tap({
        next: val => {
          if (val) {
            console.log(val, 'authenticated');
            return of(true); // or if you want Observable replace true with of(true)
          }
          console.log(val, 'acces denied!');
          return of(false); // or if you want Observable replace true with of(true)
        },
        error: error => {
          let redirectRoute: string;
          if (error.status === 401) {
            redirectRoute = '/error/401';
            this.router.navigateByUrl(redirectRoute);
          } else if (error.status === 403) {
            redirectRoute = '/error/403';
            this.router.navigateByUrl(redirectRoute);
          }
        },
        complete: () => console.log('completed!')
      })
    );
  }
}

어떤 경우에는 적어도 operatornext일부가 작동하지 않을 수 있습니다 . 그것을 제거하고 아래와 같이 오래된 상품을 추가하십시오 .tapmap

  public canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {
    return this.auth.pipe(
      map((auth) => {
        if (auth) {
          console.log('authenticated');
          return true;
        }
        console.log('not authenticated');
        this.router.navigateByUrl('/login');
        return false;
      }),
      tap({
        error: error => {
          let redirectRoute: string;
          if (error.status === 401) {
            redirectRoute = '/error/401';
            this.router.navigateByUrl(redirectRoute);
          } else if (error.status === 403) {
            redirectRoute = '/error/403';
            this.router.navigateByUrl(redirectRoute);
          }
        },
        complete: () => console.log('completed!')
      })
    );
  }

0

다른 구현 방법을 보여주기 위해. 문서에 따라 다른 답변에서 언급 한 CanActivate의 반환 유형은 부울로 확인되는 Promise 일 수도 있습니다.

참고 : 표시된 예는 Angular 11에서 구현되었지만 Angular 2+ 버전에 적용됩니다.

예:

import {
  Injectable
} from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  CanActivateChild,
  Router,
  RouterStateSnapshot,
  UrlTree
} from '@angular/router';
import {
  Observable
} from 'rxjs/Observable';
import {
  AuthService
} from './auth.service';

@Injectable()
export class AuthGuardService implements CanActivate, CanActivateChild {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(
    route: ActivatedRouteSnapshot, state: RouterStateSnapshot
  ): Observable < boolean | UrlTree > | Promise < boolean | UrlTree > | boolean | UrlTree {
    return this.checkAuthentication();
  }

  async checkAuthentication(): Promise < boolean > {
    // Implement your authentication in authService
    const isAuthenticate: boolean = await this.authService.isAuthenticated();
    return isAuthenticate;
  }

  canActivateChild(
    childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot
  ): Observable < boolean | UrlTree > | Promise < boolean | UrlTree > | boolean | UrlTree {
    return this.canActivate(childRoute, state);
  }
}


0

async await를 사용하여 ... 약속이 해결되기를 기다립니다.

async getCurrentSemester() {
    let boolReturn: boolean = false
    let semester = await this.semesterService.getCurrentSemester().toPromise();
    try {

      if (semester['statusCode'] == 200) {
        boolReturn = true
      } else {
        this.router.navigate(["/error-page"]);
        boolReturn = false
      }
    }
    catch (error) {
      boolReturn = false
      this.router.navigate(["/error-page"]);
    }
    return boolReturn
  }

여기 내 인증 장치입니다 (@angular v7.2)

async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    let security: any = null
    if (next.data) {
      security = next.data.security
    }
    let bool1 = false;
    let bool2 = false;
    let bool3 = true;

    if (this.webService.getCookie('token') != null && this.webService.getCookie('token') != '') {
      bool1 = true
    }
    else {
      this.webService.setSession("currentUrl", state.url.split('?')[0]);
      this.webService.setSession("applicationId", state.root.queryParams['applicationId']);
      this.webService.setSession("token", state.root.queryParams['token']);
      this.router.navigate(["/initializing"]);
      bool1 = false
    }
    bool2 = this.getRolesSecurity(next)
    if (security && security.semester) {
      // ----  watch this peace of code
      bool3 = await this.getCurrentSemester()
    }

    console.log('bool3: ', bool3);

    return bool1 && bool2 && bool3
  }

경로는

    { path: 'userEvent', component: NpmeUserEvent, canActivate: [AuthGuard], data: {  security: { semester: true } } },
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.