Angular+Firebase如何通过IsApproved字段控制用户登录权限
实现方案
问题根源说明
你遇到的两个错误核心原因一致:Firebase Auth返回的内置User对象与你存储在Firestore中自定义User集合的文档是完全独立的两个数据结构:
- 登录时
data.user是Firebase认证体系内置对象,仅包含uid、邮箱等认证基础字段,不存在你自定义的IsApproved字段,直接访问自然会抛出类型错误 - 安全规则中
request.auth.uid是字符串类型的用户ID,不包含任何自定义字段,直接取IsApproved属性必然报错
1. 登录逻辑修复
登录成功后先查询Firestore中对应当前用户uid的文档,获取你自定义的用户数据再做判断,修改后的SignIn代码如下:
import { User } from '你的User接口文件路径'; // 先导入你自定义的User接口 async SignIn(email,password) { const loading =await this.LoadingCtrl.create({ message:'Authenticating..', spinner:"crescent", showBackdrop:true }); loading.present(); this.afauth.setPersistence(firebase.default.auth.Auth.Persistence.LOCAL) .then(()=>{ this.afauth.signInWithEmailAndPassword(email,password) .then(async (data)=>{ // 这里加async支持内部await if(!data.user){ loading.dismiss(); this.toast('Please check your credentials','warning'); this.afauth.signOut(); }else{ // 查询Firestore中当前用户的自定义数据 const userDocRef = this.afs.collection('user').doc<User>(data.user.uid); const userDoc = await userDocRef.get().toPromise(); const userData = userDoc.data(); loading.dismiss(); if(userData?.IsApproved === true){ this.router.navigate(['/menu']); }else{ this.afauth.signOut(); // 未审核用户直接清除登录状态,避免越权访问 this.toast('账号尚未通过审核,请联系管理员','warning'); this.router.navigate(['/accessdenied']); } } }) .catch(error=>{ loading.dismiss(); this.toast(error.message,'danger'); }) }) .catch(error=>{ loading.dismiss(); this.toast(error.message,'danger'); }); }
如果你的RxJS版本高于7,toPromise()已废弃,可以替换为firstValueFrom():
import { firstValueFrom } from 'rxjs'; // 替换查询语句为 const userDoc = await firstValueFrom(userDocRef.get());
2. 路由守卫补充(推荐)
仅在登录环节做校验无法阻止用户直接通过URL访问受保护页面,建议新增Angular路由守卫,所有受保护路由的canActivate配置为守卫类,守卫内逻辑和登录校验一致:先判断是否登录,再查询IsApproved字段,不符合要求直接跳转accessdenied页。
3. Firestore安全规则修复
安全规则需要主动查询对应uid的用户文档校验IsApproved字段,正确规则如下:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 放行注册时的用户文档写入权限 match /user/{userId} { allow create: if request.auth != null && request.auth.uid == userId; allow read, update, delete: if request.auth != null && request.auth.uid == userId; } // 其余所有资源仅允许已审核用户访问 match /{document=**} { allow read, write: if request.auth != null && exists(/databases/$(database)/documents/user/$(request.auth.uid)) && get(/databases/$(database)/documents/user/$(request.auth.uid)).data.IsApproved == true; } } }
注意规则优先级,自定义集合的独立规则要放在通配符规则之前,避免被通配符规则覆盖。
内容的提问来源于stack exchange,提问作者Notation
相关产品推荐
相关产品推荐

