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

FCM发送数据消息一段时间后返回401错误求助

排查FCM REST API返回401错误的问题

从你的描述和代码片段来看,问题的核心在于AccessToken的过期检查逻辑缺失,导致运行一段时间后使用了过期的token发送请求,触发FCM的401 Unauthorized错误。下面具体分析并给出修复方案:

1. 问题根源:未检查AccessToken的有效期

你的getAccessToken()方法只在googleCredentials.getAccessToken()==null时才触发刷新,但Google生成的AccessToken默认有1小时(3600秒)的有效期。当token不为null但已经过期时,你仍然会使用这个失效的token发起请求,FCM自然会返回401错误。

2. 修复AccessToken刷新逻辑

修改getAccessToken()方法,增加对token过期时间的检查,确保每次发送请求前使用的都是有效的token:

public static synchronized String getAccessToken() {
    AccessToken currentToken = googleCredentials.getAccessToken();
    // 检查token是否为空,或者距离过期不足60秒(预留缓冲时间避免边缘情况)
    if (currentToken == null || System.currentTimeMillis() >= currentToken.getExpirationTime() - 60 * 1000) {
        try {
            googleCredentials.refresh();
        } catch (IOException e) {
            e.printStackTrace();
            // 建议替换为更健壮的错误处理,比如抛出自定义异常让上层处理
            throw new RuntimeException("Failed to refresh FCM access token", e);
        }
    }
    return googleCredentials.getAccessToken().getTokenValue();
}

3. 额外的代码优化建议

(1)修复static块的异常处理和资源泄漏

你的static块没有处理文件读取的异常,也没有关闭输入流,可能导致类初始化失败或资源泄漏。改用try-with-resources优化:

static {
    try (FileInputStream refreshTokenStream = new FileInputStream("C:/prj/proserviceaccoutkey.json")) {
        googleCredentials = GoogleCredentials.fromStream(refreshTokenStream)
                .createScoped("https://www.googleapis.com/auth/firebase.messaging");
        FirebaseOptions options = new FirebaseOptions.Builder()
                .setCredentials(googleCredentials).build();
        // 如果后续需要使用Firebase Admin SDK的其他功能,建议初始化FirebaseApp
        // FirebaseApp.initializeApp(options);
    } catch (IOException e) {
        throw new RuntimeException("Failed to load service account key file", e);
    }
}

(2)考虑使用Firebase Admin SDK替代手动REST调用

手动调用FCM REST API需要自己管理AccessToken,容易出错。推荐直接使用Firebase Admin SDK,它会自动处理token的刷新、过期管理,代码更简洁可靠:

// 初始化(static块中完成)
static {
    try (FileInputStream serviceAccount = new FileInputStream("C:/prj/proserviceaccoutkey.json")) {
        FirebaseOptions options = new FirebaseOptions.Builder()
                .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                .build();
        FirebaseApp.initializeApp(options);
    } catch (IOException e) {
        throw new RuntimeException("Failed to initialize Firebase Admin SDK", e);
    }
}

// 发送数据消息的方法
public static void sendDataMessage() throws FirebaseMessagingException {
    Message message = Message.builder()
            .putData("key1", "value1")
            .putData("key2", "value2")
            .setToken("DEVICE_REGISTRATION_TOKEN")
            .build();

    String response = FirebaseMessaging.getInstance().send(message);
    System.out.println("Successfully sent message: " + response);
}

4. 其他可能的排查点

  • 检查服务账号密钥文件是否有变动,比如权限被修改或密钥过期;
  • 确认你的服务器时间是否准确,时间偏差过大可能导致token过期判断出错;
  • 查看FCM控制台的错误日志,是否有更详细的401错误说明(比如token无效、权限不足等)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:48:14