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

Android端通过FCM REST API向订阅主题发推送的问题求助

问题分析与解决方案

一、当前实现的核心问题

  1. 安全风险极高:直接在Android客户端嵌入FCM密钥/令牌,APK反编译后极易泄露,可能导致他人滥用你的FCM资源发送恶意通知。
  2. API版本不匹配:你混用了FCM旧API的请求格式与v1 API的端点,必然导致请求失败:
    • 旧API(fcm/send)采用to: "/topics/xxx"格式,授权头为key=遗留服务器密钥
    • v1 API(messages:send)要求完全不同的请求结构,且授权需OAuth2令牌,无法直接在客户端生成

二、找不到服务器密钥的解决办法

Firebase控制台更新后,遗留服务器密钥的获取路径:

  1. 打开Firebase控制台进入你的项目
  2. 点击右上角设置图标 > 项目设置
  3. 切换到云消息传递标签页
  4. 拉至页面底部,找到遗留服务器密钥(注意:该密钥仅适用于旧版FCM API,不推荐生产环境使用)

若要使用更安全的FCM v1 API,无需服务器密钥,而是使用服务账号密钥生成OAuth2令牌,推荐配合Firebase Admin SDK使用(无需手动处理令牌)。

三、正确的实现方案

步骤1:搭建后端服务(必须)

所有FCM通知发送逻辑必须放在后端,以下是Node.js后端示例(基于Firebase Admin SDK):

const admin = require('firebase-admin');
const serviceAccount = require('./path-to-your-service-account-key.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});

// 发送主题通知的核心逻辑
async function sendTopicNotification(title, body, topic) {
  const message = {
    notification: {
      title: title,
      body: body
    },
    topic: topic
  };

  try {
    const response = await admin.messaging().send(message);
    console.log('通知发送成功:', response);
    return { success: true, messageId: response };
  } catch (error) {
    console.error('发送失败:', error);
    return { success: false, error: error.message };
  }
}

// 用Express暴露接口供客户端调用
const express = require('express');
const app = express();
app.use(express.json());

app.post('/send-topic-notification', async (req, res) => {
  const { title, body, topic } = req.body;
  if (!title || !body || !topic) {
    return res.status(400).json({ error: '缺少必要参数' });
  }
  const result = await sendTopicNotification(title, body, topic);
  res.json(result);
});

app.listen(3000, () => {
  console.log('后端服务运行在3000端口');
});

步骤2:Android客户端修改逻辑

客户端不再直接调用FCM API,而是请求自建后端接口:

binding.btnSend.setOnClickListener {
    val messageContent = binding.edtNotification.text.toString().trim()
    if (messageContent.isEmpty()) {
        Toast.makeText(this, "请输入通知内容", Toast.LENGTH_SHORT).show()
        return@setOnClickListener
    }

    // 构建请求体
    val requestBody = JSONObject().apply {
        put("title", "Padwa")
        put("body", messageContent)
        put("topic", "Notification")
    }

    // 替换为你的后端接口地址
    val url = "https://your-backend-domain/send-topic-notification"
    val jsonObjectRequest = object : JsonObjectRequest(
        Request.Method.POST, url, requestBody,
        { response ->
            if (response.getBoolean("success")) {
                Log.d("TAG", "通知发送成功")
                Toast.makeText(this, "通知发送成功", Toast.LENGTH_SHORT).show()
            } else {
                Log.e("TAG", "发送失败: ${response.getString("error")}")
                Toast.makeText(this, "发送失败", Toast.LENGTH_SHORT).show()
            }
        },
        { error ->
            Log.e("TAG", "请求失败: $error")
            Toast.makeText(this, "网络请求失败", Toast.LENGTH_SHORT).show()
        }
    ) {
        override fun getHeaders(): MutableMap<String, String> {
            val headers = HashMap<String, String>()
            headers["Content-Type"] = "application/json"
            // 若后端有身份验证,在此添加验证头(如管理员token)
            return headers
        }
    }

    Volley.newRequestQueue(this).add(jsonObjectRequest)
}

步骤3:获取服务账号密钥

  1. Firebase控制台 > 项目设置 > 服务账号
  2. 点击生成新的私钥,下载JSON文件并放到后端项目中

四、临时调试方案(仅开发阶段使用)

若需快速验证功能,可暂时使用旧版FCM API:

  1. 将请求URL改回https://fcm.googleapis.com/fcm/send
  2. 修改授权头:headers["Authorization"] = "key=你的遗留服务器密钥"
  3. 保留原请求格式(to: "/topics/Notification")

注意:此方案绝对不能用于生产环境,密钥泄露风险极高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 11:00:07