繁体   English   中英

使CanActivate等待Subject计算

[英]Make CanActivate wait for Subject to compute

我正在尝试在我的Angular + Firebase应用程序中实现身份验证防护。

我正在使用Firebase身份验证(FA)和Firebase Cloud Firestore(FCF)的组合来保存用户。 因此,仅仅FA用户还不够,我需要在FCF中找到相同的用户才能考虑用户登录。

在注册期间,我在FC中将用户保存在与FA中该用户的uid相同的id下。

在我的authentication.service

    export class AuthenticationService {

      public authenticatedUserSubject: Subject<null | User> = new Subject <null | User>();

      public constructor(
        private readonly angularFireAuth: AngularFireAuth,
        private readonly angularFirestore: AngularFirestore
      ) {    
        this.angularFireAuth.authState.subscribe(    
          (userInfo: null | UserInfo) => {    
            if (userInfo === null) {    
              this.authenticatedUserSubject.next(null);    
            } else {    
              this.angularFirestore.collection<User>('users').doc<User>(userInfo.uid).valueChanges().pipe(take(1)).subscribe(
                (user: undefined | User): void => {   
                  if (typeof user === 'undefined') {
                    this.authenticatedUserSubject.next(null);
                  } else {
                    this.authenticatedUserSubject.next(user);
                  }
                }
              );
            }
          }
        ); 
      }   
    }

根据我的理解,当服务初始化时,这段代码将开始收听FA用户的更改,然后我将检查FCF用户并更新authenticatedUserSubject

在我的authenticated.guard

    public canActivate(activatedRouteSnapshot: ActivatedRouteSnapshot, routerStateSnapshot: RouterStateSnapshot): Observable<boolean> {
        return this.authenticationService.authenticatedUserSubject.pipe(take(1)).pipe(  
          map( 
            (user: null | User): boolean => {
              if (user === null) {
                this.router.navigateByUrl('/sign-in');
                return false;
              } else {
                return true;
              }
            }
          )
        );
      }

问题:

  • 警卫不会从主题中获得任何价值
  • 如果我使用BehaviourSubject而不是Subject,初始值为null ,刷新页面将发出null作为默认值,将用户抛给/sign-in

目标:

  • 在刷新/加载应用程序时,应在路由防护运行之前计算authenticatedUserSubject
  • 注销时,警卫应该知道状态变化

另外,如果有更好的方法来实现这一点,请告诉我!

我猜你可以使用BehaviorSubject,初始值为null,但是你所要做的就是以下面的方式使用takeWhile

public canActivate(activatedRouteSnapshot: ActivatedRouteSnapshot, routerStateSnapshot: RouterStateSnapshot): Observable<boolean> {

    return this.authenticationService.authenticatedUserSubject.pipe(
    tap((user: User) => {
       if(!user){
          this.router.navigate(['/sign-up']);
       }
    }),
    takeWhile((data) => data === null, true)
    );

  }

如您所知,方法需要布尔值,因此您可以在管道中的takeWhile之前映射它。

希望这对你有用,因为我有同样的问题而且有效。

您应该使用BehaviourSubject而不是Subject 主体不向订户发出最后发射的值。 主题订阅者只有在订阅后的Subject.next()时才会获得价值。 BehaviourSubject不是这种情况。 订阅者将始终获得订阅的最后一次发布值。 有了这个让我们尝试改进你的代码,看看你的问题是否解决:

像这样改变你的服务 -

export class AuthenticationService {

    public authenticatedUserSubject: BehaviorSubject<User> = new BehaviorSubject<User>(null);

    public constructor(
      private readonly angularFireAuth: AngularFireAuth,
      private readonly angularFirestore: AngularFirestore
    ) {


        combineLatest(this.angularFireAuth.authState, 
                      this.angularFirestore.collection<User>('users').doc<User>(userInfo.uid).valueChanges()
                     )
                    .pipe(
                        tap(([userFromFireAuth, userFromFirestore]) => {
                            if (!userFromFireAuth) {
                                this.authenticatedUserSubject.next(null); 
                            } else {
                                if (typeof userFromFireAuth === 'undefined') {
                                    this.authenticatedUserSubject.next(null);
                                } else {
                                    this.authenticatedUserSubject.next(userFromFireAuth);
                                }
                            }
                        })
                    ).subscribe();       
    }   
  }

这样可以避免subscribe()链接

现在让我们像这样重写canActivate:

public canActivate(activatedRouteSnapshot: ActivatedRouteSnapshot, routerStateSnapshot: RouterStateSnapshot): Observable<boolean> {

        return this.authenticationService.authenticatedUserSubject
                   .pipe(
                       //this will wait until user becomes NOT NULL
                       skipWhile(user => !user),
                       tap(user => {
                            if (!user) {
                                //this will be useful when user logged out and trying to reach a page which requires authentication
                                //NOTICE - When page is NOT refreshed
                                this.router.navigate(['/sign-up']);
                            }
                        }),
                        take(1),
                        map(() => true)
                   );                   
      }

如果有效,请告诉我们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM