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

如何在React项目中使用Firebase创建自定义令牌实现用户名登录

实现Firebase用户名登录的代码放置方案

核心前置说明

你提到的两个步骤要拆分到不同端实现,Firebase Admin SDK相关逻辑绝对不能放到前端,会导致服务账号密钥泄露,必须放在服务端运行。

1. 自定义令牌生成逻辑(服务端)

在Next.js项目的pages/api目录下新建create-custom-token.js接口,专门负责校验用户名密码、生成自定义令牌,参考代码如下:

import admin from 'firebase-admin'

// 仅服务端初始化Admin SDK,密钥存环境变量,不要硬编码
if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert(JSON.parse(process.env.FIREBASE_ADMIN_SERVICE_ACCOUNT)),
  })
}

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: '不支持的请求方法' })
  }
  const { username, password } = req.body
  try {
    // 替换为你自己的用户名密码校验逻辑,密码需加密存储,不要明文比对
    const userQuery = await admin.firestore().collection('users').where('username', '==', username).get()
    if (userQuery.empty) {
      return res.status(401).json({ error: '用户名或密码错误' })
    }
    const userDoc = userQuery.docs[0]
    const userData = userDoc.data()
    if (userData.encryptedPassword !== yourEncryptFunction(password)) {
      return res.status(401).json({ error: '用户名或密码错误' })
    }
    // 生成自定义令牌
    const customToken = await admin.auth().createCustomToken(userDoc.id)
    return res.status(200).json({ customToken })
  } catch (e) {
    return res.status(500).json({ error: e.message })
  }
}

2. 客户端登录逻辑(useUser.js添加代码)

你标注的位置确实是放置用户名登录逻辑的位置,修改后的完整useUser.js代码如下:

import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import firebase from "firebase/app";
import "firebase/auth";

import initFirebase from "../../config";
import {
  removeUserCookie,
  setUserCookie,
  getUserFromCookie,
} from "./userCookie";

initFirebase();

export const mapUserData = async (user) => {
  const { uid, email } = user;
  const token = await user.getIdToken(true);
  return {
    id: uid,
    email,
    token,
  };
};

const useUser = () => {
  const [user, setUser] = useState();
  const router = useRouter();

  // 新增用户名登录逻辑
  const loginWithUsername = async (username, password) => {
    try {
      // 请求服务端接口换取自定义令牌
      const res = await fetch('/api/create-custom-token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
      })
      const { customToken } = await res.json()
      // 用自定义令牌完成Firebase登录
      const userCredential = await firebase.auth().signInWithCustomToken(customToken)
      const userData = await mapUserData(userCredential.user)
      setUser(userData)
      setUserCookie(userData)
      // 登录成功后跳转
      router.push('/dashboard')
    } catch (e) {
      console.error('登录失败:', e)
      throw e
    }
  }

  const logout = async () => {
    return firebase
      .auth()
      .signOut()
      .then(() => {
        router.push("/");
      })
      .catch((e) => {
        console.error(e);
      });
  };

  useEffect(() => {
    const cancelAuthListener = firebase
      .auth()
      .onIdTokenChanged(async (userToken) => {
        if (userToken) {
          const userData = await mapUserData(userToken);
          setUserCookie(userData);
          setUser(userData);
        } else {
          removeUserCookie();
          setUser();
        }
      });

    const userFromCookie = getUserFromCookie();
    if (!userFromCookie) {
      return;
    }
    setUser(userFromCookie);
    return () => cancelAuthListener;
  }, []);
  // 导出登录方法供登录页调用
  return { user, logout, loginWithUsername };
};

export { useUser };

后续你直接在登录表单页面调用loginWithUsername方法传入用户名和密码即可完成登录,原有登录状态监听、cookie同步逻辑无需修改,可直接复用。

内容的提问来源于stack exchange,提问作者Mahier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 22:39:04