Я пытаюсь использовать защиту маршрутизатора Angular2, чтобы ограничить доступ к некоторым страницам в моем приложении. Я использую аутентификацию Firebase. Для того , чтобы проверить , если пользователь вошел в систему с 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
не отображает запрашиваемую страницу для пользователя. Я предполагаю, что это потому, что я использую обратный вызов, но я не могу понять, как это сделать иначе. Есть предположения?
источник
Ответы:
canActivate
необходимо вернуть,Observable
что завершает:@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
источник
auth
- это значение, испускаемое наблюдаемым (может быть простоtrue
илиfalse
). Наблюдаемое выполняется, когда маршрутизатор подписывается на него. Возможно, что-то не хватает в вашей конфигурации.Вы можете использовать
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); }); } } }
источник
canActivate
может вернуть,Promise
которыйboolean
тоже решаетисточник
Вы можете вернуть 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); } } }) }) } }
источник
return this.authService.getAccessRights().then...
и вернуть логический результат без переносаresolve
.Чтобы развернуть наиболее популярный ответ. API Auth для AngularFire2 несколько изменился. Это новая подпись для достижения 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, чтобы узнать, хотите ли вы протестировать более подробный аспект пользователя. Но должно, по крайней мере, помочь защитить маршруты от пользователей, которые не вошли в систему.
источник
В самой последней версии 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; } }) ); } }
источник
В моем случае мне нужно было обрабатывать другое поведение в зависимости от ошибки статуса ответа. Вот как это работает у меня с 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!') }) ); } }
В некоторых случаях это может не сработать, по крайней мере, со
next
стороныtap
оператора . Удалите его и добавьте старый добрый,map
как показано ниже: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!') }) ); }
источник
Для того, чтобы показать другой способ реализации. Согласно документации и упомянутым в других ответах возвращаемый тип 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); } }
источник
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 }
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 } } },
источник