Angular 基于两个可观察值的守卫
Angular guard based on two observables
我正在尝试做一个“AdminGuard”,它应该基于两件事:
- 用户是否登录?
- 用户是否有管理员权限?
我有一个 AuthService
,它提供两个 Observable
。
我做了以下事情:
@Injectable({
providedIn: 'root'
})
export class IsAdminGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) { }
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> {
console.log(this.auth)
return combineLatest([this.auth.isLoggedIn, this.auth.isAdmin]).pipe(
take(1),
map((authInfo) => {
console.log(authInfo)
if (!authInfo[0]) {
console.error('Access denied - Unauthorized')
return this.router.parseUrl('/auth/');
} else if (!authInfo[1]) {
console.error('Access denied - Admin only')
return this.router.parseUrl('/auth/unauthorized');
} else {
return true;
}
})
);
}
}
console.log(this.auth)
被调用并且似乎具有有效值,但第二个 console.log 从未被调用并且我的组件未加载。
如果我从我的路线中移除守卫:
{
path: 'admin',
component: AdminComponent,
//canActivate: [IsAdminGuard],
}
它有效,所以我很确定是 IsAdminGuard 不起作用。
我还显示了一些基于相同布尔值的其他东西(一些 *ngIf="authService.IsLoggedIn | async"
正在工作,所以我真的不明白我搞砸了什么?
编辑
以下是我如何在我的 AuthService 中更新 IsLoggedIn/IsAdmin/IsUser 的不同值:
constructor(public afAuth: AngularFireAuth, public router: Router, private afStore: AngularFirestore) {
this.afAuth.authState.subscribe(async user => {
console.log('handling auth')
if (this._roleSubscription) {
this._roleSubscription.unsubscribe();
this._roleSubscription = undefined;
}
if (user) {
this._user.next(user);
this._isLoggedIn.next(true);
this._roleSubscription = this.afStore.doc<Roles>(`roles/${user.uid}`).valueChanges().subscribe(role => {
console.log('updating roles', role)
if (role) {
this._isAdmin.next(role.admin == true)
this._isUser.next(role.admin == true || role.user == true);//Admin have also an heart, they are users too!
} else {
this._isAdmin.next(false);
this._isUser.next(false);
}
});
} else {
this._user.next(undefined);
this._isLoggedIn.next(false);
this._isAdmin.next(false);
this._isUser.next(false);
await this.router.navigate(['/auth']);
}
console.log('values updated')
})
}
您必须使用发出最新值的 ReplaySubject
。主题仅在有活动订阅时才会发出,而 BehaviorSubject
总是在以初始值
开始时发出
readonly _isLoggedIn = new ReplaySubject<boolean>(1);
readonly _isAdmin = new ReplaySubject<boolean>(1);
我正在尝试做一个“AdminGuard”,它应该基于两件事:
- 用户是否登录?
- 用户是否有管理员权限?
我有一个 AuthService
,它提供两个 Observable
。
我做了以下事情:
@Injectable({
providedIn: 'root'
})
export class IsAdminGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) { }
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> {
console.log(this.auth)
return combineLatest([this.auth.isLoggedIn, this.auth.isAdmin]).pipe(
take(1),
map((authInfo) => {
console.log(authInfo)
if (!authInfo[0]) {
console.error('Access denied - Unauthorized')
return this.router.parseUrl('/auth/');
} else if (!authInfo[1]) {
console.error('Access denied - Admin only')
return this.router.parseUrl('/auth/unauthorized');
} else {
return true;
}
})
);
}
}
console.log(this.auth)
被调用并且似乎具有有效值,但第二个 console.log 从未被调用并且我的组件未加载。
如果我从我的路线中移除守卫:
{
path: 'admin',
component: AdminComponent,
//canActivate: [IsAdminGuard],
}
它有效,所以我很确定是 IsAdminGuard 不起作用。
我还显示了一些基于相同布尔值的其他东西(一些 *ngIf="authService.IsLoggedIn | async"
正在工作,所以我真的不明白我搞砸了什么?
编辑 以下是我如何在我的 AuthService 中更新 IsLoggedIn/IsAdmin/IsUser 的不同值:
constructor(public afAuth: AngularFireAuth, public router: Router, private afStore: AngularFirestore) {
this.afAuth.authState.subscribe(async user => {
console.log('handling auth')
if (this._roleSubscription) {
this._roleSubscription.unsubscribe();
this._roleSubscription = undefined;
}
if (user) {
this._user.next(user);
this._isLoggedIn.next(true);
this._roleSubscription = this.afStore.doc<Roles>(`roles/${user.uid}`).valueChanges().subscribe(role => {
console.log('updating roles', role)
if (role) {
this._isAdmin.next(role.admin == true)
this._isUser.next(role.admin == true || role.user == true);//Admin have also an heart, they are users too!
} else {
this._isAdmin.next(false);
this._isUser.next(false);
}
});
} else {
this._user.next(undefined);
this._isLoggedIn.next(false);
this._isAdmin.next(false);
this._isUser.next(false);
await this.router.navigate(['/auth']);
}
console.log('values updated')
})
}
您必须使用发出最新值的 ReplaySubject
。主题仅在有活动订阅时才会发出,而 BehaviorSubject
总是在以初始值
readonly _isLoggedIn = new ReplaySubject<boolean>(1);
readonly _isAdmin = new ReplaySubject<boolean>(1);