簡體   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