Angular2 canActivate()调用异步函数

Eva*_*ter 59 typescript angular2-routing angular

我正在尝试使用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;
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

当导航到对其具有防护的页面时authenticated,或者not authenticated打印到控制台(在等待来自firebase的响应的一些延迟之后).但是,导航永远不会完成.此外,如果我没有登录,我将被重定向到该/login路线.因此,我遇到的问题是return true不向用户显示请求的页面.我假设这是因为我正在使用回调,但我无法弄清楚如何做到这一点.有什么想法吗?

Gün*_*uer 94

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
    }
}
Run Code Online (Sandbox Code Playgroud)

有一个return缺失,我使用map()而不是subscribe()因为subscribe()返回一个Subscription不是Observable


小智 16

您可能会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);
      });
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这实际上是一个很好的答案,对我很有帮助。即使我有类似的问题,但接受的答案并没有解决我的问题。这个做了 (2认同)

小智 14

canActivate可以返回一个Promise能解决一个boolean


Aji*_*esh 6

在最新版本的 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;
        }
      })
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Rud*_*dom 5

扩展最流行的答案。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;
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:这是一个相当幼稚的测试。您可以控制台记录用户实例,以查看是否要针对用户的一些更详细的方面进行测试。但至少应帮助保护针对未登录用户的路由。


rog*_*lad 5

您可以返回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);
      }
    }

     })
   })
  }
}
Run Code Online (Sandbox Code Playgroud)


ris*_*thi 5

为了展示另一种实现方式。根据文档,以及其他答案提到的 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);
  }
}
Run Code Online (Sandbox Code Playgroud)