AngularFirebaseAuth: вызов API сервера сразу после аутентификации firebase? - PullRequest
0 голосов
/ 19 октября 2018

Моя аутентификация основана на 2 вещах:

  • firebase auth (электронная почта / пароль)
  • вызов API сервера для получения полной сущности клиента из BDD и из firebaseID (пользовательдолжен существовать) Таким образом, пользователь будет «аутентифицирован», если эти два условия будут выполнены.

У меня также есть authGuards на основе isAuthenticated(), возвращающего Observable (потому что при обновлении страницы охранник должен ждатьдля завершения аутентификации перед перенаправлением пользователя куда-либо).

Проблема: я не могу найти способ заставить это работать со всеми асинхронными и rxjs беспорядками / адом. В настоящее время это работает, но каждый раз *Вызывается 1012 *, аутентификация serverAPI вызывается каждый раз ... Как мне выполнить рефакторинг для того, чтобы вызывать сервер только один раз и все асинхронное / перезагрузить все еще работает?

AuthService:

export class AuthService {
    public userRole: UserBoRole;
    public authState$: Observable<firebase.User>;

    constructor(
        private afAuth: AngularFireAuth,
        private snackBar: SnackBarService,
        private translate: TranslateService,
        private router: Router,
        private grpcService: GrpcService
    ) {
        this.authState$ = this.afAuth.authState.pipe(
            take(1),
            mergeMap(user => {
                if (!user) {
                    return of(user);
                }

                // User is successfully logged in,
                // now we need to check if he has a correct role to access our app
                // if an error occured, consider our user has not logged in, so we return null
                return this.checkProfile().pipe(
                    take(1),
                    map(() => {
                        this.test = true;
                        return user;
                    }),
                    catchError(err => {
                        console.error(err);
                        return of(null);
                    })
                );
            })
        );

        // Subscribing to auth state change. (useless here because access logic is handled by the AuthGuard)
        this.authState$.subscribe(user => {
            console.log('authState$ changed :', user ? user.toJSON() : 'not logged in');
        });
    }

    checkProfile() {
        return this.callAuthApi().pipe(
            map((customer) => {
                if (!customer || customer.hasRole() === "anonymous") {
                    return Promise.reject(new Error(AuthService.AUTH_ERROR_ROLE));
                }
                this.userRole = customer.getRole();
            })
        );
    }

    isAuthenticated(): Observable<boolean> {
        return this.authState$.pipe(map(authState => !!authState));
    }
}

AuthGuard:

export class AuthGuard implements CanActivate, CanActivateChild {
    constructor(private authService: AuthService, private router: Router) {}

    check(): Observable<boolean> {
        return this.authService.isAuthenticated().pipe(
            catchError(err => {
                // notifying UI of the error
                this.authService.handleAuthError(err);

                // signout user
                this.authService.signOut();

                // if an error occured, consider our user has not logged in
                return of(false);
            }),
            tap(isAuthenticated => {
                if (!isAuthenticated) {    
                    // redirecting to login
                    this.router.navigate(['login']);
                }
            })
        );
    }

    canActivateChild(): Observable<boolean> {
        return this.check();
    }

    canActivate(): Observable<boolean> {
        return this.check();
    }
}

Спасибо

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Хаха, ReactiveX не из легких.У него довольно крутая кривая обучения.Но это действительно мощно.

1.вызвать сервер только один раз

Вы можете использовать shareReplay.

Чтобы понять, как работает shareReplay, посмотрите здесь https://ng -rxjs-share-replay.stackblitz.io

//shareReplay example
ngOnInit() {    
    const tods$ = this.getTodos();
    tods$.subscribe(console.log);// 1st sub
    tods$.subscribe(console.log);// 2st sub
}

getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.url)
  .pipe(
    tap(() => console.log('Request')),
    shareReplay(1) // compare with comment and uncomment
  );
}  

Вывод с помощью shareReplay

Request
[Object, Object, Object]
[Object, Object, Object]

Вывод без shareReplay

Request
[Object, Object, Object]
Request
[Object, Object, Object]

Вы можете использовать shareReplay в своем сервисном коде аутентификации.

//auth.services.ts
import { shareReplay } from 'rxjs/operators';
...

this.user$ = this.afAuth.authState.pipe(
    tap(user => {
        console.log('login user$ here', user)
    }),
    switchMap(user => {
        if (user) {
            //do something
            return this.db.object(`users/${user.uid}`).valueChanges();
        } else {
            return of(null);
        }
    }),
    shareReplay(1)  //**** this will prevent unnecessary request****
);

2.асинхронно и жду toPromise()

//auth.service.ts
...
getUser() {
    return this.user$.pipe(first()).toPromise();
}

//auth.guard.ts
...
async canActivate(next: ActivatedRouteSnapshot
  , state: RouterStateSnapshot
): Promise<boolean> {

  const user = await this.auth.getUser();
  //TODO your API code or other conditional authentication here

  if (!user) {
    this.router.navigate(['/login']);
  }
  return !!user;    
}

Надеюсь, это поможет вам.

0 голосов
/ 21 октября 2018

Вы можете изменить функцию checkProfile(), чтобы она возвращала наблюдаемое вместо наблюдаемого из запроса http или обещания в случае ошибки.Сначала вы проверите, прошел ли пользователь проверку подлинности (я предполагал, что userRole будет в порядке, поскольку вы сохраняете его после вызова back-end), и если да, верните вновь созданную наблюдаемую без вызова на back-end, иначе вы сделаете запрос и отправитеваш наблюдаемый на основе результата HTTP-вызова.В следующем примере вы будете звонить только один раз:

checkProfile() {
  return new Observable((observer) => {
    if (this.userRole) {
      observer.next();
      observer.complete();
    } else {
      this.callAuthApi().pipe(
          map((customer) => {
            if (!customer || customer.hasRole() === "anonymous") {
              observer.error(new Error(AuthService.AUTH_ERROR_ROLE));
              observer.complete();
            }
            this.userRole = customer.getRole();
            observer.next();
            observer.complete();
          })
      );
    }
  });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...