我的身份验证基于两件事:
- firebase 身份验证(电子邮件/密码)
- 调用服务器 API 以从 BDD 和 firebaseID 检索完整的客户实体(用户必须存在) 因此,如果满足这两个条件,用户将被“认证”。
我还有一个基于返回 Observable 的 isAuthenticated()
的 authGuard(因为在页面刷新时,守卫必须等待身份验证完成,然后才能将用户重定向到任何地方)。
问题:我找不到一种方法让它与所有异步和 rxjs 困惑/ hell 一起工作。目前它正在工作,但每次调用 isAuthenticated
时,都会调用 serverAPI auth时间...
我怎样才能重构它以便只调用服务器一次并且所有异步/重新加载的东西仍然有效?
授权服务:
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));
}
}
授权 guard :
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();
}
}
谢谢
最佳答案
您可以更改您的 checkProfile()
函数以在出现错误时返回 observable 而不是来自 http 请求或 promise 的 observable。首先,您将检查用户是否已经通过身份验证(我假设 userRole 会很好,因为您在调用后端后保存它)如果是,则返回一个新创建的可观察对象而不调用您的后端,否则您将发出请求并发出您可观察到基于 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();
})
);
}
});
}
关于javascript - AngularFirebaseAuth : Calling server api just after firebase auth?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52896245/