Angular Service Worker+Firebase推送报错messaging/mismatched-credential求助
解决Firebase Cloud Messaging的
messaging/mismatched-credential错误 你遇到的这个错误,核心问题在于你错误地从PushSubscription的endpoint里提取了所谓的"FCM token"——但这个值根本不是FCM认可的注册令牌。FCM的设备令牌是由Firebase Messaging SDK直接生成的,和Service Worker的PushSubscription完全是两个不同的东西,把它们混在一起用自然会触发凭据不匹配的错误。
下面是具体的修正步骤:
1. 前端:改用Firebase Messaging SDK获取真正的FCM令牌
你的MessagingService应该放弃从PushSubscription拆token的做法,直接用Firebase Messaging SDK生成并存储FCM令牌:
import { Injectable, OnInit } from '@angular/core'; import { AngularFireDatabase } from 'angularfire2/database'; import { AngularFireAuth } from 'angularfire2/auth'; import * as firebase from 'firebase'; import 'rxjs/add/operator/take'; import { BehaviorSubject } from 'rxjs'; import { SwPush } from '@angular/service-worker'; @Injectable() export class MessagingService implements OnInit { currentMessage = new BehaviorSubject(null); private messaging = firebase.messaging(); // 初始化Firebase Messaging实例 constructor( private db: AngularFireDatabase, private afAuth: AngularFireAuth, private swPush: SwPush ) { } ngOnInit(): void { // 监听前台收到的推送消息 this.messaging.onMessage((payload) => { console.log('[App] 收到推送消息', payload); this.currentMessage.next(payload); }); } subscribeToPush() { // 先请求用户的通知权限 Notification.requestPermission().then(permission => { if (permission === 'granted') { // 获取真正的FCM设备令牌 this.messaging.getToken().then(fcmToken => { console.log('FCM令牌已获取:', fcmToken); this.updateToken(fcmToken); // 将令牌存储到数据库 // 如果需要同时维护Service Worker的订阅,可以继续保留这部分逻辑 this.swPush.requestSubscription({ serverPublicKey: '你的VAPID公钥' // 从Firebase控制台获取 }).then(pushSubscription => { console.log('Service Worker订阅成功:', pushSubscription); }); }); } }); } updateToken(fcmToken: string) { return this.afAuth.authState.take(1).subscribe(user => { if (!user) return; // 存储FCM令牌,而不是PushSubscription对象 const tokenData = { [user.uid]: fcmToken }; this.db.object('fcmTokens/').update(tokenData) .then(() => console.log('FCM令牌已成功存储到数据库')) .catch(err => console.log('存储失败:', err)); }); } sendMessage() { const randomId = 'msg' + Math.floor(Math.random() * 100).toString(); const message = { [randomId]: { title: '测试标题', body: '测试内容' } }; return this.db.object('messages/tEqlfyFohYV4221ACmbWxZZAGTi2/').update(message); } }
2. 云函数:直接使用存储的FCM令牌发送通知
现在数据库里存的是有效的FCM令牌,云函数不需要再做字符串拆分,直接读取使用即可:
import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; // 初始化Firebase Admin,默认会自动加载项目凭据,无需手动传入config admin.initializeApp(); interface MessagePayload { body: string; title: string; } exports.msgNotif = functions.database.ref('/messages/{userId}/{messageId}').onCreate( async (snapshot, context) => { // 使用async/await让代码更简洁易读 console.log('触发推送通知函数'); const messageData = <MessagePayload>snapshot.val(); const userId = context.params.userId; // 从数据库获取用户的FCM令牌 const tokenSnapshot = await admin.database().ref(`/fcmTokens/${userId}`).once('value'); const fcmToken = tokenSnapshot.val(); if (!fcmToken) { console.log(`用户${userId}未找到有效的FCM令牌`); return null; } const notificationPayload = { notification: { title: messageData.title, body: messageData.body, clickAction: '/' // 点击通知后跳转的页面 } }; try { const response = await admin.messaging().sendToDevice(fcmToken, notificationPayload); console.log('通知发送结果:', response.results); // 处理单个令牌的发送错误 response.results.forEach((result, idx) => { if (result.error) { console.error(`发送到令牌${fcmToken}失败:`, result.error); } }); } catch (err) { console.error('发送通知时发生错误:', err); } return null; } );
3. 额外的验证步骤
- 确认VAPID密钥正确:在Firebase控制台的「项目设置 > Cloud Messaging」页面,找到Web应用配置部分,生成或复制你的VAPID公钥,替换前端代码中的
serverPublicKey。 - 检查服务账号权限:在Google Cloud控制台的「IAM与管理员」中,确保云函数使用的服务账号拥有「Cloud Messaging Admin」角色。
- 清理无效数据:删除数据库中之前存储的PushSubscription数据,避免后续混淆。
内容的提问来源于stack exchange,提问作者madnero
相关产品推荐
相关产品推荐

