如何隐藏Firebase FCM Service Worker密钥并解决onMessage失效问题
Firebase FCM 隐藏Service Worker密钥并恢复onMessage触发的解决方案
核心问题说明
- Service Worker属于静态资源,无法直接读取前端框架(如Next.js)的环境变量,硬编码密钥存在安全风险。
- 删除Service Worker中的Firebase配置后,FCM的messaging实例未初始化,推送事件处理链路断裂,导致页面中
onMessage函数无法正常触发。
解决方案步骤
1. 页面端传递Firebase配置
页面可正常读取环境变量,初始化FCM后,将配置发送给已激活的Service Worker。
2. Service Worker动态初始化
让Service Worker监听页面发送的配置消息,完成Firebase初始化,确保推送事件处理逻辑正常运行。
修改后的代码示例
/src/components/Message.tsx
import React, { useEffect, useState } from 'react'; import { messageState } from '@/libs/recoilState'; import { initializeApp } from 'firebase/app'; import { getMessaging, getToken, onMessage } from 'firebase/messaging'; import { useRecoilState } from 'recoil'; export interface MessageType { title: string; reservationId: string; description: string; [key: string]: string; } const Message = () => { const [message, setMessage] = useRecoilState<MessageType>(messageState); const onMessageFCM = async () => { const permission = await Notification.requestPermission(); if (permission !== 'granted') return; // 读取环境变量中的Firebase配置 const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_API_KEY, authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN, projectId: process.env.NEXT_PUBLIC_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_MESSAGING_ID, appId: process.env.NEXT_PUBLIC_APP_ID, measurementId: process.env.NEXT_PUBLIC_MEASUREMENT_ID, }; const firebaseApp = initializeApp(firebaseConfig); const messaging = getMessaging(firebaseApp); // 向Service Worker发送配置 if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.ready; registration.active?.postMessage({ type: 'SET_FIREBASE_CONFIG', config: firebaseConfig }); } getToken(messaging, { vapidKey: process.env.NEXT_PUBLIC_VAPID_KEY, }) .then((currentToken) => { if (currentToken) { console.log(currentToken); } else { console.log('No registration token available. Request permission to generate one.'); } }) .catch((err) => { console.log('An error occurred while retrieving token. ', err); }); onMessage(messaging, (payload) => { console.log('Message received. ', payload); setMessage(payload?.data as MessageType); }); }; useEffect(() => { onMessageFCM(); }, []); return <>></>; }; export default Message;
public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/9.14.0/firebase-app-compat.js'); importScripts('https://www.gstatic.com/firebasejs/9.14.0/firebase-messaging-compat.js'); let messaging = null; // 监听页面发送的配置消息 self.addEventListener('message', (event) => { if (event.data?.type === 'SET_FIREBASE_CONFIG' && !messaging) { firebase.initializeApp(event.data.config); messaging = firebase.messaging(); console.log('Firebase initialized in Service Worker'); } }); self.addEventListener('push', function(event) { console.log("处理收到的推送数据并显示为通知的内容") // 可选:自定义通知展示逻辑 if (event.data) { const payload = event.data.json(); event.waitUntil( self.registration.showNotification(payload.notification.title, { body: payload.notification.body, icon: '/firebase-logo.png' }) ); } }); self.addEventListener('notificationclick', function(event) { console.log("点击弹出的通知窗口时要处理的内容") event.notification.close(); // 可选:打开指定页面 event.waitUntil( clients.openWindow('/') ); });
关键注意事项
- 环境变量必须以
NEXT_PUBLIC_为前缀,Next.js仅会将此类变量暴露给客户端代码。 - 代码中通过
navigator.serviceWorker.ready确保Service Worker激活后再发送配置,避免初始化失败。 - 若Service Worker已提前激活,可能需要刷新页面让配置生效,也可在sw中添加本地存储逻辑缓存配置,减少重复发送。
内容的提问来源于stack exchange,提问作者Yeongjun Kim
相关产品推荐
相关产品推荐

