Angular AuthGuard - 按顺序检查身份验证和数据库条目

时间:2018-01-31 17:34:08

标签: angular firebase angularfire2 angular5

要允许访问管理员路由,我必须检查两件事:

  1. 如果用户已通过身份验证
  2. 如果此用户是管理员。我从firebase数据库获得了admin状态。
  3. AdminGuard

    import {ActivatedRouteSnapshot, CanActivate, Router, 
    RouterStateSnapshot} from '@angular/router';
    import {Observable} from 'rxjs/Observable';
    import {AngularFireAuth} from 'angularfire2/auth';
    import {Injectable} from '@angular/core';
    import {DbActionsService} from '../services/db-actions.service';
    import {AuthService} from '../services/auth.service';
    import 'rxjs/add/operator/map';
    
    @Injectable()
    export class AdminGuard implements CanActivate {
    
      constructor(private afAuth: AngularFireAuth, private router: Router, private dbAction: DbActionsService, private authService : AuthService) {}
    
      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
        if (this.afAuth.auth.currentUser) {
          return this.dbAction.getUserProfileData(this.afAuth.auth.currentUser.email).map((user) => {
            if (user[0].admin) {
              return true;
            } else {
              this.router.navigate(['/']);
              return false;
            }
          }).take(1)
        } else {
          this.router.navigate(['/']);
        }
      }
    }
    

    服务功能

    getUserProfileData(userEmail: string) {
        this.dataRef = this.afDatabase.list('data/users', ref => ref.orderByChild('profile/email').equalTo(userEmail));
        this.data = this.dataRef.snapshotChanges().map(changes => {
          return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
        });
        return this.data;
      }
    

    这很好用,但是,我的主要问题是当我刷新(或最初加载)具有AdminGuard的页面时,它总是将我重定向到主页,因为AdminGuard不等待身份验证响应。

    我尝试了什么

    新的AuthService

    import { Injectable } from '@angular/core';
    import {AngularFireAuth} from 'angularfire2/auth';
    import {Observable} from 'rxjs/Observable';
    
    @Injectable()
    export class AuthService {
      private user: any;
    
      constructor(private afAuth: AngularFireAuth) { }
    
      setUser(user) {
        this.user = user;
      }
      getAuthenticated(): Observable<any> {
        return this.afAuth.authState;
      }
    
    }
    

    新的AdminGuard

    import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
    import {Observable} from 'rxjs/Observable';
    import {AngularFireAuth} from 'angularfire2/auth';
    import {Injectable} from '@angular/core';
    import {DbActionsService} from '../services/db-actions.service';
    import {AuthService} from '../services/auth.service';
    import 'rxjs/add/operator/map';
    
    @Injectable()
    export class AdminGuard implements CanActivate {
    
      constructor(private afAuth: AngularFireAuth, private router: Router, private dbAction: DbActionsService, private authService : AuthService) {}
    
      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
    
        return this.authService.getAuthenticated().map(user => {
          this.authService.setUser(user);
          return user ? true : false;
        });
    
      }
    }
    

    这可以在初始加载时使用Auth Check查找,但是我如何实现数据库条件以检查用户是否是管理员?我不知道......

1 个答案:

答案 0 :(得分:4)

这是一个应该根据您的代码工作的警卫,让我们分解它:

  1. 获取用户authState可观察的内容。
  2. 管道switchMap以切换到您想要的数据库数组observable。
  3. 使用双重爆炸将admin属性映射到boolean。
  4. 使用点按来处理重定向。
  5. import { tap, map, switchMap, take } from 'rxjs/operators;
    
    // ...omitted
    
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
    
      return this.afAuth.authState.pipe(
        take(1),
        switchMap(user => {
          return this.dbAction.getUserProfileData(user.email)
        })
        map(profile => !!(profile.length && profile[0].admin),
        tap(isAdmin => {
          if (isAdmin) {
            console.log('admin user, you shall pass')
          } else {
            console.log('non-admin user, go home')
            this.router.navigate(['/']);
          }
        })
      )
    
    }
    

    我已经使用Angular / Firebase完成了许多防护实施,并对最有效的方法有一些看法 - 我认为role based auth screencast可能会帮助您改进当前的方法