You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:57:53