Flutter集成FCM推送调用v1接口返回401错误求助
FCM HTTP v1接口调用返回401错误的解决方法
问题描述
在Flutter应用中集成FCM推送时,调用FCM HTTP v1接口返回401认证错误,无法正常发送推送。当前实现的sendPushMessage函数代码如下:
void sendPushMessage(String token, String body, String title) async { Dio dio = Dio(); try { await dio.post( 'https://fcm.googleapis.com/v1/projects/ecommerece-c1601/messages:send', options: Options( headers: { "Authorization": "Bearer ${ApiKeys.fcmServerKey}", "Content-Type": "application/json" }, ), data: { "priority": "high", "data": { "click_action": "FLUTTER_NOTIFICATION_CLICK", "status": "done", "body": body, "title": title, }, "message": { "token": token, "notification": { "title": title, "body": body, "android_channel_id": "Shoppie" } }, "android": { "notification": {"click_action": "TOP_STORY_ACTIVITY"} }, "apns": { "payload": { "aps": {"category": "NEW_MESSAGE_CATEGORY"} } } }); } catch (e) { print("error"); log(e.toString()); } }
已完成的配置:
- 按FCM从Legacy API迁移到HTTP v1的文档配置了接口地址,
ecommerece-c1601为项目ID - 在Firebase和Google Cloud API服务中启用了FCM API
- 环境变量中配置了
GOOGLE_APPLICATION_CREDENTIALS,对应Firebase项目设置中的私钥 - 已启用Cloud Messaging API (Legacy),代码中使用了对应的Server Key
返回的错误信息:
[log] DioException [bad response]: This exception was thrown because the response has a status code of 401 and RequestOptions.validateStatus was configured to throw for this status code. The status code of 401 has the following meaning: "Client error - the request contains bad syntax or cannot be fulfilled" Read more about status codes at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status In order to resolve this exception you typically have either to verify and fix your request code or you have to fix the server code.
解决方法
1. 核心问题:认证方式错误
FCM HTTP v1接口不支持Legacy Server Key,必须使用Google OAuth2访问令牌。你当前用Legacy Server Key作为Bearer令牌,这是导致401错误的根本原因。
2. 生成OAuth2访问令牌
使用googleapis_auth包生成短期的FCM访问令牌,步骤如下:
首先在pubspec.yaml添加依赖:
dependencies: googleapis_auth: ^1.4.1 http: ^1.1.0 dio: ^5.3.0 # 确保dio版本兼容
然后实现获取令牌的函数:
import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:googleapis_auth/auth_io.dart'; import 'package:http/http.dart' as http; Future<String> getFcmAccessToken() async { final serviceAccountPath = Platform.environment['GOOGLE_APPLICATION_CREDENTIALS']; if (serviceAccountPath == null) { throw Exception("GOOGLE_APPLICATION_CREDENTIALS环境变量未配置"); } final serviceAccountJson = json.decode(File(serviceAccountPath).readAsStringSync()); final serviceAccount = ServiceAccountCredentials.fromJson(serviceAccountJson); final scopes = ['https://www.googleapis.com/auth/firebase.messaging']; final client = await clientViaServiceAccount(serviceAccount, scopes); final accessToken = client.credentials.accessToken.data; client.close(); return accessToken; }
3. 修改推送函数
更新sendPushMessage函数,替换认证头,同时修正请求数据结构(v1接口要求data、android、apns等字段嵌套在message对象内):
import 'package:dio/dio.dart'; import 'dart:developer' as dev; void sendPushMessage(String token, String body, String title) async { Dio dio = Dio(); try { final fcmToken = await getFcmAccessToken(); await dio.post( 'https://fcm.googleapis.com/v1/projects/ecommerece-c1601/messages:send', options: Options( headers: { "Authorization": "Bearer $fcmToken", "Content-Type": "application/json" }, ), data: { "message": { "token": token, "notification": { "title": title, "body": body, "android_channel_id": "Shoppie" }, "data": { "click_action": "FLUTTER_NOTIFICATION_CLICK", "status": "done", "body": body, "title": title, }, "android": { "notification": {"click_action": "TOP_STORY_ACTIVITY"} }, "apns": { "payload": { "aps": {"category": "NEW_MESSAGE_CATEGORY"} } } }, "priority": "high" }, ); print("推送发送成功"); } catch (e) { print("推送发送失败"); dev.log(e.toString()); } }
4. 额外检查项
- 确认服务账号拥有Firebase Cloud Messaging Admin角色:在Google Cloud控制台的IAM页面,找到对应服务账号,添加
Firebase Cloud Messaging Admin角色 - 验证项目ID
ecommerece-c1601与Firebase项目的实际ID一致 - 确保服务账号密钥文件未过期,且配置的环境变量路径正确
内容的提问来源于stack exchange,提问作者Mohanad Abdallah
相关产品推荐
相关产品推荐

