it-swarm.com.de

Angular2 canActivate () ruft eine async-Funktion auf

Ich versuche, Angular2 Router Guards zu verwenden, um den Zugriff auf einige Seiten in meiner App zu beschränken. Ich verwende die Firebase-Authentifizierung. Um zu überprüfen, ob ein Benutzer bei Firebase angemeldet ist, muss ich .subscribe() für das FirebaseAuth-Objekt mit einem Rückruf aufrufen. Dies ist der Code für die Wache:

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;
        });
    }
}

Wenn Sie zu einer Seite navigieren, auf der sich der Guard befindet, wird entweder authenticated oder not authenticated auf die Konsole gedruckt (nach einiger Verzögerung auf die Antwort von Firebase). Die Navigation wird jedoch nie abgeschlossen. Wenn ich nicht angemeldet bin, werde ich zur /login-Route umgeleitet. Das Problem, das ich habe, ist return true zeigt dem Benutzer die angeforderte Seite nicht an. Ich gehe davon aus, dass ich einen Rückruf verwende, aber ich kann nicht herausfinden, wie ich es anders machen kann. Irgendwelche Gedanken?

38
Evan Salter

canActivate muss eine Observable zurückgeben, die Folgendes ausführt:

@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
    }
}

Es fehlt eine return und ich verwende map() anstelle von subscribe(), da subscribe() eine Subscription und keine Observable zurückgibt.

72

canActivate kann eine Promise zurückgeben, die auch eine boolean auflöst

11
paulsouche

Sie können Observable verwenden, um den asynchronen Logikteil zu behandeln. Hier ist der Code, den ich zum Beispiel teste: 

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);
      });
    }
  }
}
6
KobeLuo

Um die populärste Antwort zu erweitern. Die Auth-API für AngularFire2 wurde etwas geändert. Dies ist eine neue Signatur, um einen AngularFire2 AuthGuard zu erhalten:

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;
    });
  }
}

Hinweis: Dies ist ein ziemlich naiver Test. Sie können die Benutzerinstanz über die Konsole protokollieren, um zu prüfen, ob Sie einen detaillierteren Aspekt des Benutzers testen möchten. Aber sollte zumindest dazu beitragen, Routen vor nicht angemeldeten Benutzern zu schützen.

3
Rudi Strydom

Sie können true | false als Versprechen zurückgeben.

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);
      }
    }

     })
   })
  }
}
2
Muhammad Aslam

In der neuesten Version von AngularFire funktioniert der folgende Code (bezogen auf die beste Antwort). Beachten Sie die Verwendung der "Pipe" -Methode.

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;
        }
      })
    );
  }
}

1
Ajitesh