[英]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;
}
}
)
);
}
问题:
null
,刷新页面将发出null
作为默认值,将用户抛给/sign-in
目标:
另外,如果有更好的方法来实现这一点,请告诉我!
我猜你可以使用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.