Angular+Firebase按登录用户ID存储数据的实现及刷新异常问题
问题根因定位
你遇到的刷新丢数问题本质是异步执行时序竞争:
- AngularFireAuth提供的
authState是异步回调,页面刷新时的执行顺序为:组件构造函数初始化→触发ngOnInit生命周期→同步执行getEmployees()方法→此时authState的回调尚未返回,this.userId仍为初始的undefined - 此时实际发起的Firebase请求路径是
employees/undefined,完全匹配不到你之前存在employees/{真实用户uid}下的业务数据,因此列表为空 - 后续
authState回调触发、将this.userId赋值为真实用户ID时,之前发起的getEmployees请求已经执行完毕,不会自动重新拉取数据,最终表现为刷新后数据全部消失 - 首次登录、浏览器返回时能正常显示,是因为此时用户认证状态已经完成本地缓存,
userId提前完成了赋值,getEmployees执行时可以拼接出正确的数据库路径 - 原有实现还存在安全隐患:仅靠前端拼接uid的路径做隔离,没有后端规则校验的话,用户可以通过篡改前端代码读取其他账号的数据,隔离效果仅停留在表面
适配现有代码的修复方案
Service层改造:绑定认证状态与数据请求,解决时序问题
删除原先独立的userId成员变量,把数据请求逻辑和authState流做串联,等用户认证状态确定后再发起数据库请求,彻底避免空值请求的问题:
@Injectable({ providedIn: 'root' }) export class EmployeeService { constructor( public firebase: AngularFireDatabase, private datePipe: DatePipe, private afu: AngularFireAuth, public clientService: ClientService, public contractsService: ContractsService, public maintainerService: MaintainerService ) {} // 原有成员变量、form定义、initializeFormGroup方法完全保留,不做修改 getEmployees(){ return this.afu.authState.pipe( // 过滤未登录的空状态,等认证结果返回 filter(user => !!user), // 拿到真实uid后再拼接路径发起数据库请求 switchMap(user => { this.employeeList = this.firebase.list(`employees/${user.uid}`); return this.employeeList.snapshotChanges(); }) ); } }
- 这个逻辑下不管什么时候调用
getEmployees(),都会等authState返回明确的用户信息后,再发起对应路径的请求,不会出现空值路径的问题 - 当用户登录、登出、刷新页面时,只要认证状态变化,流就会自动重新发起正确的请求,不需要额外手动维护赋值逻辑
组件层适配:补全订阅销毁逻辑
原有组件的列表渲染逻辑不需要大改,只需要补充组件销毁时的订阅取消,避免内存泄漏:
import { Subject, takeUntil } from 'rxjs'; export class YourTargetComponent implements OnInit, OnDestroy { // 原有组件成员变量(sort、paginator、displayColumns、listData等)完全保留 private destroy$ = new Subject<void>(); ngOnInit(): void { this.service.getEmployees() .pipe(takeUntil(this.destroy$)) .subscribe(list => { // 原有列表映射、MatTableDataSource赋值、过滤器配置逻辑完全不变 let array = list.map(item => { let clientName = this.clientService.getClientName(item.payload.val()['client']); let maintainerName = this.maintainerService.getMaintainerName(item.payload.val()['maintainer']); return { $key: item.key, clientName, maintainerName, ...item.payload.val() }; }); this.listData = new MatTableDataSource(array); this.listData.sort = this.sort; this.listData.paginator = this.paginator; this.listData.filterPredicate = (data, filter) => { return this.displayColumns.some(ele => { return ele != 'actions' && data[ele].toLowerCase().indexOf(filter) != -1; }); } }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } }
数据库层配置:落地真正的用户数据隔离
仅靠前端代码按uid拼路径无法做到真正的隔离,需要在Firebase Realtime Database的规则中补充权限校验,从服务端层面阻止跨账号访问:
按业务实际路径配置规则,核心逻辑是仅允许用户读写自己uid对应的路径下的数据:
{ "rules": { "employees": { "$uid": { ".read": "auth.uid === $uid", ".write": "auth.uid === $uid" } }, "clients": { "$uid": { ".read": "auth.uid === $uid", ".write": "auth.uid === $uid" } }, "maintainers": { "$uid": { ".read": "auth.uid === $uid", ".write": "auth.uid === $uid" } }, "contracts": { "$uid": { ".read": "auth.uid === $uid", ".write": "auth.uid === $uid" } } } }
- 配置完成后,即使用户篡改前端代码尝试请求其他用户路径的数据,数据库也会直接拒绝响应,从根源上保障数据隔离的安全性
内容的提问来源于stack exchange,提问作者puzzy.x
相关产品推荐
相关产品推荐

