Заставить CanActivate ждать, пока субъект вычислит - PullRequest
0 голосов
/ 20 июня 2019

Я пытаюсь реализовать защиту аутентификации в своем приложении Angular + Firebase.

Я использую комбинацию Firebase Authentication (FA) и Firebase Cloud Firestore (FCF) для сохранения пользователей.Поэтому просто пользователя FA недостаточно, мне нужно найти того же пользователя в FCF, чтобы считать пользователя вошедшим в систему.

Во время регистрации я сохраняю пользователей в FCF под id, какuid этого пользователя в FA.

В моем 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 должен быть рассчитан до запуска средства защиты маршрута
  • При выходе из системыохранник должен знать об изменении состояния

Также, если есть лучший способ реализовать это, пожалуйста, дайте мне знать!

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Вы должны использовать BehaviourSubject вместо Subject.Субъект не отправляет подписчику последнее излученное значение.Подписчик 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)
                   );                   
      }

Сообщите нам, работает ли он.

0 голосов
/ 20 июня 2019

Полагаю, вы можете использовать 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 в трубе.

Надеюсь, это сработает, потому что у меня возникла та же проблема, и она сработала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...