我正在尝试找到/创建一种正确(最佳)的方式来获取和使用 Angular 应用程序中的自定义声明。我通过云功能添加了管理员自定义声明。我现在想要的(以及我试图做的)现在是:
- 在一项服务中获取索赔(和登录用户)(例如
auth.service
) - 允许需要读取声明的所有其他组件通过该服务中的简单 API 执行此操作
- 不让其他组件订阅 authState 或其他任何内容(只需同步读取 my 的属性
auth.service
)
我为什么要这个?- 因为我相信它更具可读性和更易于维护
authState
(通过只在一个地方(例如)读取(订阅authService.ts
),从而使维护更容易,并允许其他组件从authService.ts
属性/字段同步读取声明)
所以,我现在正在做的代码(它不工作......见POINTS_IN_CODE):
auth.service.ts
// imports omitted for brevity...
@Injectable()
export class AuthService {
user: Observable<User> = of(null);
uid: string;
claims: any = {};
claimsSubject = new BehaviorSubject(0);
constructor(private afAuth: AngularFireAuth,
private afStore: AngularFirestore,
private functions: AngularFireFunctions) {
this.afAuth.authState
.subscribe(
async authUser => {
if (authUser) { // logged in
console.log(`Auth Service says: ${authUser.displayName} is logged in.`);
this.uid = authUser.uid;
this.claims = (await authUser.getIdTokenResult()).claims;
// POINT_IN_CODE_#1
this.claimsSubject.next(1);
const userDocumentRef = this.afStore.doc<User>(`users/${authUser.uid}`);
// if provider is Google (or Facebook <later> (OR any other 3rd party))
// document doesn't exist on the first login and needs to be created
if (authUser.providerData[0].providerId === 'google.com') {
userDocumentRef.get()
.subscribe( async snapshot => {
if ( ! snapshot.exists) { // if the document does not exist
console.log(`\nNew document being created for: ${authUser.displayName}...`); // create a user document
await userDocumentRef.set({name: authUser.displayName, email: authUser.email, provider: 'google.com'});
}
});
}
this.user = userDocumentRef.valueChanges();
}
else { // logged out
console.log('Auth Service says: no User is logged in.');
}
}
);
}
login(email, password): Promise<any> {
return this.afAuth.auth.signInWithEmailAndPassword(email, password);
}
hasClaim(claim): boolean {
return this.hasAnyClaim([claim]);
}
hasAnyClaim(paramClaims): boolean {
for (let paramClaim of paramClaims) {
if (this.claims[paramClaim]) {
return true;
}
}
return false;
}
}
登录组件.ts
// imports...
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
form: FormGroup;
hide = true;
errorMessage = '';
loading = false;
constructor(private fb: FormBuilder,
public authService: AuthService,
private router: Router) {}
ngOnInit() {
this.logout();
this.form = this.fb.group({
username: ['test@test.te', Validators.compose([Validators.required, Validators.email])],
password: ['Asdqwe123', Validators.compose([Validators.required])]
});
}
submit() {
this.loading = true;
this.authService.login(this.form.value.username, this.form.value.password)
.then(resp => {
this.loading = false;
// POINT_IN_CODE_#2
// what I am doing right now, and what doesn't work...
this.authService.user
.subscribe(
resp => {
if (this.authService.hasClaim('admin')) {
this.router.navigate(['/admin']);
}
else {
this.router.navigate(['/items']);
}
}
);
// POINT_IN_CODE_#3
//this.authService.claimsSubject
// .subscribe(
// num => {
// if (num === 1) {
// if (this.authService.hasClaim('admin')) {
// this.router.navigate(['/admin']);
// }
// else {
// this.router.navigate(['/items']);
// }
// }
// });
}
logout() {
this.authService.logout();
}
}
POINTS_IN_CODE
在auth.service.ts
at POINT_IN_CODE_#1
- 我有想法从这个主题发出claimsSubject
并在login.component.ts
atPOINT_IN_CODE_#3
订阅它并知道,如果它的值为1
,则声明auth.service.ts
已从authState
.
在我知道我可以从中得到索赔,login.component.ts
但它只是“感觉”不正确......这就是这个问题的主要内容......POINT_IN_CODE_#2
resp.getIdTokenResult
我可能要问的具体问题是:
admin
如果他有“管理员”自定义声明,我希望能够在登录页面后重定向用户。
正如我上面所说的(如果可能并且如果它是好的/提高可读性/提高维护能力),我想这样做,而不是直接订阅authState
,而是通过auth.service.ts
.
例如,我会使用相同的“逻辑”来制作一个AuthGuard
只会调用authService.hasClaim('admin')
的,而不必订阅authState
自己来进行检查。
注意 我想知道我做的方式是否好,是否有任何警告或只是简单的改进。欢迎所有建议和评论,所以请发表评论,尤其是我的为什么我想要这个?部分!
Edit-1: 添加了打字稿代码突出显示并指出了我的代码中不能按我想要的方式工作的确切位置。
编辑 2: 编辑了一些关于我的 authService.user 为空的原因的评论......(我运行了一些代码,在登录组件中检查之前将其设置为空......)