如何在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
相关产品推荐
相关产品推荐

