Ich versuche, Angular2-Router-Schutzvorrichtungen zu verwenden, um den Zugriff auf einige Seiten in meiner App einzuschränken. Ich verwende die Firebase-Authentifizierung. Um zu überprüfen, ob ein Benutzer bei Firebase angemeldet ist, muss ich .subscribe()
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 Schutz befindet authenticated
, oder not authenticated
die auf der Konsole gedruckt wird (nach einiger Verzögerung warten Sie auf die Antwort von Firebase). Die Navigation wird jedoch nie abgeschlossen. Wenn ich nicht angemeldet bin, werde ich zur /login
Route weitergeleitet. Das Problem, das ich habe, ist return true
, dass dem Benutzer die angeforderte Seite nicht angezeigt wird. Ich gehe davon aus, dass dies daran liegt, dass ich einen Rückruf verwende, aber ich kann nicht herausfinden, wie ich es anders machen soll. Irgendwelche Gedanken?
quelle
Antworten:
canActivate
muss eine zurückgebenObservable
, die abgeschlossen ist:@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
return
fehlt ein und ich benutzemap()
stattsubscribe()
weilsubscribe()
gibt einSubscription
nicht ein zurückObservable
quelle
auth
ist ein Wert, der vom Observable ausgegeben wird (möglicherweise nurtrue
oderfalse
). Das Observable wird ausgeführt, wenn der Router es abonniert. Möglicherweise fehlt in Ihrer Konfiguration etwas.Sie können
Observable
den asynchronen Logikteil verwenden. 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); }); } } }
quelle
canActivate
zurückgeben kann einenPromise
Entschluss , dass einboolean
zuquelle
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); } } }) }) } }
quelle
Um die beliebteste Antwort zu erweitern. Die Auth-API für AngularFire2 hat sich 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 in der Konsole protokollieren, um festzustellen, ob Sie einen Test anhand eines detaillierteren Aspekts des Benutzers durchführen möchten. Sollte aber zumindest dazu beitragen, Routen vor Benutzern zu schützen, die nicht angemeldet sind.
quelle
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; } }) ); } }
quelle
In meinem Fall musste ich mit unterschiedlichem Verhalten umgehen, abhängig vom Antwortstatusfehler. So funktioniert es bei mir mit 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!') }) ); } }
In einigen Fällen könnte dies nicht funktionieren, zumindest den
next
Teil dertap
Betreiber . Entfernen Sie es und fügen Sie altes Gutmap
wie unten hinzu: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!') }) ); }
quelle