javascript - 如何在启动 Angular 应用程序之前等待 Firebase 身份验证
问题描述
我想在firebase身份验证检索用户令牌时显示一个小的加载徽标,然后开始“真正”单页应用程序。
到目前为止,我有一个身份验证服务:
constructor(
public afAuth: AngularFireAuth,
) {
this.afAuth.onAuthStateChanged(user => {
if (user) {
this.setCredentials(user)
}
})
}
setCredentials(user: firebase.User) {
return user.getIdTokenResult(true).then(idTokenResult => {
this.credentials = {
userId: idTokenResult.claims.id,
role: idTokenResult.claims.role,
token: idTokenResult.token,
};
// STARTS THE APPLICATION NOW ?
})
}
是否有可能实现这种行为?我已经阅读了有关 APP_INITIALIZER 的信息,但没有成功。我想避免本地存储/会话存储,而是完全依赖这个初始化。
更新:
创建了一个初始化函数:
export function initApp(auth: AuthService, afAuth: AngularFireAuth) {
return () => {
return new Promise((resolve) => {
afAuth.user.pipe(
take(1),
).subscribe(user => {
if (user) {
auth.setCredentials(user)
.then(() => resolve())
} else {
resolve();
}
})
});
}
}
并编辑了 AppModule 提供者:
providers: [
interceptorProviders /* my interceptors */,
{
provide: APP_INITIALIZER,
useFactory: initApp,
deps: [AuthService, AngularFireAuth],
multi: true
}
]
仍然需要弄清楚如何添加等待标志,但这是另一个问题。我会尽快更新。
解决方案
回答我自己的问题
总而言之,我想确保在处理路由之前将与 firebase 用户关联的令牌声明(角色和用户 ID)存储在我的身份验证服务中,因为这些路由中的组件将使用这些凭据。
最后我没有遵循 APP_INITIALIZER 这不是一个很好的解决方案。
认证服务
private _credentials: BehaviorSubject<Credentials> = new BehaviorSubject<Credentials>(null);
public readonly credentials$: Observable<Credentials> = this._credentials.asObservable();
constructor(private afAuth: AngularFireAuth) {
this.afAuth.authState.subscribe(user => {
this._credentials.next(null);
if (user) {
user.getIdTokenResult().then(data => {
const credentials = {
role: data.claims.role,
token: data.token,
userId: data.claims.userId
}
this._credentials.next(credentials);
console.log(credentials);
})
} else {
this._credentials.next({role: null, token: null, userId: null});
}
})
}
get credentials(): Credentials {
return this._credentials.value;
}
在 app.component 中显示一个等待的微调器
如果未设置凭据,则下面会阻止显示路由。在模板中:
<div *ngIf="!(credentials$ | async)" class="logged-wrapper">
<div class="spinner-wrapper">
<mat-spinner class="spinner"></mat-spinner>
</div>
</div>
<router-outlet *ngIf="(credentials$ | async)"></router-outlet>
在组件中:
credentials$: Observable<any>;
constructor(
private auth: AuthService,
) {
this.credentials$ = this.auth.credentials$;
}
认证卫士
takewhile 让我可以确保在进一步操作之前设置我的凭据。
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):Promise<boolean> {
return new Promise((resolve) => {
this.auth.credentials$.pipe(
takeWhile(credentials => credentials === null),
).subscribe({
complete: () => {
const credentials = this.auth.credentials
if (!credentials.role) {
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } })
resolve(false);
}
if (next.data.roles && next.data.roles.indexOf(credentials.role) === -1) {
this.router.navigate(['/']);
resolve(false);
}
resolve(true)
}
})
})
}
推荐阅读
- node.js - 使用节点 js 检查 mongodb 中的唯一电子邮件
- ssl - 由 nginx 代理的 gerrit 仅在“托管”在顶级目录上时才有效
- javascript - 为什么 chrome.runtime.lastError 不进入
- ruby-on-rails - 为什么弹窗不显示也不删除页面?
- javascript - 从组件内的路由器链接中的单击事件调用方法(Vue.js)
- c++ - 为什么将 const ptr 分配给 const 引用时不会出现编译错误?
- hibernate - Spring JPA java.lang.ClassCastException:java.lang.Integer 无法转换为 com.gpch.hotel.model.User
- configuration - 尝试在远程服务器上配置弹性搜索时出现“master_not_discovered_exception”
- typescript - 为什么 TypeScript 在强制转换时不会抱怨状态中未包含的属性?
- c++ - 我怎样才能从数组中只返回几个值