Firebase邮箱密码认证异常:登录后无法访问数据库路径
问题描述
Firebase新手,已成功调用登录POST接口,Firebase控制台显示用户已认证,但网站端用户未自动完成认证,无法访问/recipes路径(该路径从Realtime Database获取数据)。
Firebase安全规则
{ "rules": { ".read": "auth != null", ".write": "auth != null" } }
Angular登录方法代码
// 全局通知用户登录状态 user = new Subject<User>() login(email: string, password: string) { // 调用Firebase登录接口 return this.http.post<AuthResponseData>('https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[MyAPIkey]', { email: email, password: password, returnSecureToken: true }).pipe(tap(resData => { this.handleAuthentication(resData.email, resData.localId, resData.idToken) })) } private handleAuthentication(email: string, id: string, tokenId: string) { let now = new Date().getTime() / 1000 let user = new User(email, id, tokenId, (3600 * now)) // 怀疑这两行有问题 user.token this.user.next(user) }
User模型代码
export class User { constructor( public email: string, public id: string, private _token: string, private _tokenExpirationDate: number) { } get token() { if (!this._tokenExpirationDate || new Date().getTime() > this._tokenExpirationDate) { return null } else { console.log("hello this is returned!") return this._token } } }
推测因auth为null导致无法访问数据,需修改或添加哪些代码让用户认证后正常访问/recipes路径?
解决方案
1. 修复Token过期时间计算错误
你的handleAuthentication方法中,Token过期时间的计算逻辑完全错误:
// 错误写法:把当前时间戳(秒)乘以3600,导致过期时间远早于当前时间,token会直接返回null let user = new User(email, id, tokenId, (3600 * now)) // 正确写法:当前时间戳(毫秒) + 1小时有效期对应的毫秒数 let expirationDate = new Date().getTime() + 3600 * 1000; let user = new User(email, id, tokenId, expirationDate);
User类的token getter用毫秒级时间戳做比较,之前的计算会让_tokenExpirationDate远小于当前时间,导致token始终返回null,后续请求无法携带有效认证信息。
2. 确保请求Realtime Database时携带Token
如果继续用原生HTTP请求访问数据库,每次请求必须在URL附加auth参数,把用户的ID Token传过去:
// 获取/recipes数据的示例 getRecipes() { // 从Subject中获取当前用户实例 let currentUser: User | undefined; this.user.subscribe(user => currentUser = user).unsubscribe(); if (!currentUser?.token) { return throwError('用户未认证'); } return this.http.get(`https://你的数据库地址.firebaseio.com/recipes.json?auth=${currentUser.token}`); }
3. 推荐用Firebase Angular SDK(更可靠)
手动调用REST接口容易遗漏认证上下文维护,建议使用@angular/fire库,它会自动关联用户认证状态与数据库请求:
- 安装依赖:
npm install @angular/fire firebase - 登录逻辑简化为:
import { AngularFireAuth } from '@angular/fire/compat/auth'; constructor(private afAuth: AngularFireAuth) {} login(email: string, password: string) { return this.afAuth.signInWithEmailAndPassword(email, password) .then(userCredential => { userCredential.user?.getIdToken().then(token => { const expirationDate = new Date().getTime() + 3600 * 1000; this.user.next(new User(userCredential.user!.email!, userCredential.user!.uid, token, expirationDate)); }); }); }
访问数据库时无需手动加Token,SDK会自动处理:
import { AngularFireDatabase } from '@angular/fire/compat/database'; constructor(private db: AngularFireDatabase) {} getRecipes() { return this.db.list('/recipes').valueChanges(); }
4. 持久化认证状态(可选但必要)
用户刷新页面后,当前认证状态会丢失,需要在应用初始化时从本地存储恢复:
// 在AuthService构造函数中添加 constructor() { const storedUser = localStorage.getItem('user'); if (storedUser) { const user = JSON.parse(storedUser); if (user.token) { this.user.next(user); } } } // 修改handleAuthentication,保存用户信息到本地存储 private handleAuthentication(email: string, id: string, tokenId: string) { const expirationDate = new Date().getTime() + 3600 * 1000; const user = new User(email, id, tokenId, expirationDate); localStorage.setItem('user', JSON.stringify(user)); this.user.next(user); }
内容的提问来源于stack exchange,提问作者Issac Howard
相关产品推荐
相关产品推荐

