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

Posted

技术标签:

【中文标题】Angularfire2 基于角色的授权,支持页面刷新【英文标题】:Angularfire2 role based authorization with support for page refresh 【发布时间】:2018-04-10 06:19:37 【问题描述】:

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

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

我的(相关)代码:

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】:

我假设您将服务和守卫都导入到您的 AppModule 中(如果您将其导入到 CoreModule 中也可以,但我的建议需要额外的步骤)。

无论哪种方式,除非您将服务注入/传递到 AppModule 的构造函数中,否则在 AdminGuard 首次调用服务之前,您的服务不会被创建/实例化。考虑到这一点,您正在订阅,并且 canActivate 函数没有及时运行(这是一个异步事件),特别是考虑到您正在获取第一个输出。

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

【讨论】:

我试过了,但没用。我将constructor (private authService: AuthService) 添加到应用程序模块并将AdminGuard 添加到模块提供程序,但是当执行防护时用户对象仍然为空:(。 幽默一下,并在您的服务控制台日志中更新您的用户行为主题前后的值与下一个。调用 canActivate 后,控制台也会记录日志。我确信您正在尝试做太多事情(映射身份验证状态以查询并从 FB 返回一些其他对象,然后更新您的用户存储)。有了这些日志,我敢打赌,您会发现您没有及时为警卫获取您的用户个人资料。考虑到这一点,也许修改是使用两个单独的 observables。一个是您查询配置文件的那个,另一个是身份验证状态。

以上是关于Angularfire2 基于角色的授权,支持页面刷新的主要内容,如果未能解决你的问题,请参考以下文章

多级基于角色授权的数据库设计

Laravel 中基于角色的 ACL

9.5 授权系统

Asp.net核心身份声明与基于角色的授权[重复]

使用 Azure AD 的 Asp.net core mvc 基于角色的授权

基于 Spring Boot / Spring Security 角色的授权无法正常工作 [重复]