[英]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.