基于Angularfire2角色的授权,支持页面刷新

时间:2017-10-28 23:19:21

标签: angular typescript firebase asynchronous angularfire2

我正在尝试使用Angularfire2和Angular4为学校作业构建基于角色的授权。但是,当我刷新页面时,我会重定向到登录页面,因为在Angularfire从Firebase数据库获取用户(+角色)之前,Guard会执行。 我想让最终用户重新加载页面而不会被重定向到登录页面。

我搜索了很多网站,但找不到任何有用的东西。许多站点解释了如何支持重新加载但不使用用户角色(因此没有Firebase数据库查询),而其他站点解释了如何使用多个用户角色但不支持页面重新加载。

我的(相关)代码:

auth.service.ts

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import { User } from '../models/user.model';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/take';

@Injectable()
export class AuthService {
    user: BehaviorSubject<User> = new BehaviorSubject(null);

    constructor (private afAuth: AngularFireAuth, private db: AngularFireDatabase) {
        this.afAuth.authState
            .switchMap(auth => {
                console.log(auth);
                if (auth) {
                    /// signed in
                    return this.db.object('users/' + auth.uid);
                } else {
                    /// not signed in
                    return Observable.of(null);
                }
            })
            .subscribe(user => {
                this.user.next(user);
            });
    }

    googleLogin () {
        const provider = new firebase.auth.GoogleAuthProvider();
        return this.afAuth.auth.signInWithPopup(provider)
                   .then(credential => {
                       return this.updateUser(credential.user);
                   });
    }

    signOut () {
        return this.afAuth.auth.signOut();
    }

    private updateUser (authData) {
        const userData = new User(authData);
        const ref = this.db.object('users/' + authData.uid);
    return ref.take(1).subscribe(user => {
            if (!user.name) {
                ref.update(userData);
            }
        });
    }
}

guard.ts

import {Injectable} from '@angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {AuthService} from '../shared/services/auth.service';
import * as _ from 'lodash';

import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';

@Injectable()
export class AdminGuard implements CanActivate {
    constructor (private auth: AuthService, private router: Router) {}

    canActivate (next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
        return this.auth.user
                   .take(1)
                   .map(user => _.get(_.get(user, 'roles'), 'admin'))
                   .do(authorized => {
                       if (!authorized) {
                           this.router.navigate(['/login']);
                       }
                   });
    }
}

提前致谢!

1 个答案:

答案 0 :(得分:0)

我假设您正在将服务和警卫导入您的AppModule(如果您将其导入CoreModule也很好,但我的建议还需要额外的工序)。

无论哪种方式,除非您将服务注入/传递到AppModule的构造函数中,否则您的服务不会被创建/实例化,直到AdminGuard首次调用它为止。考虑到这一点,您可以为canActivate功能进行订阅以及未及时运行(它是异步事件),尤其是考虑到您正在进行第一次输出。

重点是你应该将服务注入Module构造函数。