Google 本地设备组在 Android 上的云消息传递 (GCM) 给出 HTTP 错误代码 401
Google Cloud Messaging (GCM) with local device groups on Android gives HTTP Error code 401
我正在尝试让 Google 云消息传递 (GCM) 在 Android 上与本地设备组一起工作,如 https://developers.google.com/cloud-messaging/android/client-device-group 中所述。
我在 Web 控制台 (https://console.developers.google.com) 中启用了以下服务:
- Google Android
的云消息传递
- Google云Pub/Sub
- Google+ API
我还创建了以下凭据:
- API 密钥(具有指定的包名称和 SHA-1 证书密钥打印)
- OAuth 2.0 客户端 ID(Web 应用程序)
我的 Android 代码如下所示(错误处理等已被删除):
String account = ACCOUNT_NAME; // E.g. "johndoe@google.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String token = GoogleAuthUtil.getToken(context, account, scope);
// Token is successfully found here :-)
URL url = new URL("https://android.googleapis.com/gcm/notification");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("operation", "add");
requestBody.put("notification_key_name", "foobar");
requestBody.put("registration_ids",
new JSONArray(Arrays.asList(new String[]{"42", "44"})));
requestBody.put("id_token", token);
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
// connection.getResponseCode() is now 401
提交给 https://android.googleapis.com/gcm/notification 的 JSON 看起来像这样:
{
"operation": "add",
"notification_key_name": "foobar",
"registration_ids": ["42","44"],
"id_token": "[veeeeeery long string]"
}
回复内容为:
<HTML>
<HEAD>
<TITLE>Unauthorized</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Unauthorized</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
我做错了什么?
这是使用 SERVER_API_KEY 完成的方式(不是最佳实践,但我已经达到的最佳实践):
首先,获取一个应用实例id:
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// Variable "token" now has the instance id. :-)
接下来,获取消息 ID 令牌:
String account = ACCOUNT_NAME; // E.g. "johndoe@google.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String idToken = GoogleAuthUtil.getToken(context, account, scope);
// Variable "idToken" now hods the messaging id token. :-)
现在到了神奇的部分。创建一个新的设备组,成员为实例 Id:
URL url = new URL("https://gcm-http.googleapis.com/gcm/notification"); // <-- Use this URL!
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Authorization", "key=" + SERVER_API_KEY); // <--- Auth!
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("operation", "create"); // <--- Not "add"
requestBody.put("notification_key_name", "foobar");
requestBody.put("registration_ids",
new JSONArray(Arrays.asList(new String[]{token}))); // <--- Instance Id token here!
requestBody.put("id_token", idToken);
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
InputStream is = connection.getInputStream();
String responseString = new Scanner(is, "UTF-8").useDelimiter("\A").next();
is.close();
JSONObject response = new JSONObject(responseString);
Log.d(TAG, "Server response:\n" + response.toString(4));
String notificationKey = response.getString("notification_key");
此时,变量"notificationKey"保存通知密钥,它是向该设备组发送消息时必须使用的收件人。
发送这样的消息(使用 "notificationKey" 值作为参数作为来自上面的接收者):
private void sendMessage(String recipient) throws IOException, JSONException {
Log.i(TAG, "Sending message to: " + recipient);
URL url = new URL("https://gcm-http.googleapis.com/gcm/send");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
Log.d(TAG, "Opening connection to " + url.toString());
connection.setRequestProperty("Authorization", "key=" + SERVER_API_KEY);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("to", recipient);
JSONObject requestData = new JSONObject();
requestData.put("hello", "Hello World!");
requestData.put("hellodata", "42");
requestBody.put("data", requestData);
Log.d(TAG, "Request body:\n" + requestBody.toString(4));
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
Log.d(TAG, "Response code: " + connection.getResponseCode());
InputStream is = connection.getInputStream();
String responseString = new Scanner(is, "UTF-8").useDelimiter("\A").next();
is.close();
}
如果不使用服务器 API 密钥,我无法成功完成此操作。在 https://http.googleapis.com/gcm/googlenotification 端点上使用不同的授权 header 值进行调查可能是值得的。
希望这是有道理的。玩得开心。 :-)
找到诀窍:您正在使用 google 帐户获取 id_token,您需要使用与 notification_key_name 完全相同的电子邮件。所以如果你使用的是foo@gmail.com,你需要使用这个地址作为notification_key_name.
这是根据 greywolf82 的正确答案进行的总结。正确的代码应该遵循这些原则(错误处理等已被剥离):
///////////////////////////////////////////////////////////////////////////
// Working example on how to create a locally (client-side) managed //
// device group for Google Cloud Messaging. //
// //
// Thanks to greywolf82 for adding the final piece. //
// Please vote on his answer. Thank you! //
///////////////////////////////////////////////////////////////////////////
// Get token:
String account = ACCOUNT_NAME; // E.g. "johndoe@gmail.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String idToken = GoogleAuthUtil.getToken(context, account, scope);
// Get registration id:
InstanceID instanceID = InstanceID.getInstance(this);
String registration_id = instanceID.getToken(
getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// Set up HTTP connection:
URL url = new URL("https://android.googleapis.com/gcm/googlenotification");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("operation", "add");
requestBody.put("notification_key_name", ACCOUNT_NAME); // You *must* use the email!
requestBody.put("registration_ids",
new JSONArray(Arrays.asList(new String[]{registration_id})));
requestBody.put("id_token", idToken);
// Submit request body
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
// connection.getResponseCode() is now 200 :-)
// Now read the server response contents from connection.getInputStream()
提交给 https://android.googleapis.com/gcm/notification 的 JSON 看起来像这样:
{
"operation": "add",
"notification_key_name": "johndoe@gmail.com",
"registration_ids": ["very long string here"],
"id_token": "another very long string"
}
回复内容为:
{
"notification_key": "your notification key is here --- voilá!"
}
我正在尝试让 Google 云消息传递 (GCM) 在 Android 上与本地设备组一起工作,如 https://developers.google.com/cloud-messaging/android/client-device-group 中所述。
我在 Web 控制台 (https://console.developers.google.com) 中启用了以下服务:
- Google Android 的云消息传递
- Google云Pub/Sub
- Google+ API
我还创建了以下凭据:
- API 密钥(具有指定的包名称和 SHA-1 证书密钥打印)
- OAuth 2.0 客户端 ID(Web 应用程序)
我的 Android 代码如下所示(错误处理等已被删除):
String account = ACCOUNT_NAME; // E.g. "johndoe@google.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String token = GoogleAuthUtil.getToken(context, account, scope);
// Token is successfully found here :-)
URL url = new URL("https://android.googleapis.com/gcm/notification");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("operation", "add");
requestBody.put("notification_key_name", "foobar");
requestBody.put("registration_ids",
new JSONArray(Arrays.asList(new String[]{"42", "44"})));
requestBody.put("id_token", token);
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
// connection.getResponseCode() is now 401
提交给 https://android.googleapis.com/gcm/notification 的 JSON 看起来像这样:
{
"operation": "add",
"notification_key_name": "foobar",
"registration_ids": ["42","44"],
"id_token": "[veeeeeery long string]"
}
回复内容为:
<HTML>
<HEAD>
<TITLE>Unauthorized</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Unauthorized</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
我做错了什么?
这是使用 SERVER_API_KEY 完成的方式(不是最佳实践,但我已经达到的最佳实践):
首先,获取一个应用实例id:
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// Variable "token" now has the instance id. :-)
接下来,获取消息 ID 令牌:
String account = ACCOUNT_NAME; // E.g. "johndoe@google.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String idToken = GoogleAuthUtil.getToken(context, account, scope);
// Variable "idToken" now hods the messaging id token. :-)
现在到了神奇的部分。创建一个新的设备组,成员为实例 Id:
URL url = new URL("https://gcm-http.googleapis.com/gcm/notification"); // <-- Use this URL!
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Authorization", "key=" + SERVER_API_KEY); // <--- Auth!
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("operation", "create"); // <--- Not "add"
requestBody.put("notification_key_name", "foobar");
requestBody.put("registration_ids",
new JSONArray(Arrays.asList(new String[]{token}))); // <--- Instance Id token here!
requestBody.put("id_token", idToken);
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
InputStream is = connection.getInputStream();
String responseString = new Scanner(is, "UTF-8").useDelimiter("\A").next();
is.close();
JSONObject response = new JSONObject(responseString);
Log.d(TAG, "Server response:\n" + response.toString(4));
String notificationKey = response.getString("notification_key");
此时,变量"notificationKey"保存通知密钥,它是向该设备组发送消息时必须使用的收件人。
发送这样的消息(使用 "notificationKey" 值作为参数作为来自上面的接收者):
private void sendMessage(String recipient) throws IOException, JSONException {
Log.i(TAG, "Sending message to: " + recipient);
URL url = new URL("https://gcm-http.googleapis.com/gcm/send");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
Log.d(TAG, "Opening connection to " + url.toString());
connection.setRequestProperty("Authorization", "key=" + SERVER_API_KEY);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("to", recipient);
JSONObject requestData = new JSONObject();
requestData.put("hello", "Hello World!");
requestData.put("hellodata", "42");
requestBody.put("data", requestData);
Log.d(TAG, "Request body:\n" + requestBody.toString(4));
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
Log.d(TAG, "Response code: " + connection.getResponseCode());
InputStream is = connection.getInputStream();
String responseString = new Scanner(is, "UTF-8").useDelimiter("\A").next();
is.close();
}
如果不使用服务器 API 密钥,我无法成功完成此操作。在 https://http.googleapis.com/gcm/googlenotification 端点上使用不同的授权 header 值进行调查可能是值得的。
希望这是有道理的。玩得开心。 :-)
找到诀窍:您正在使用 google 帐户获取 id_token,您需要使用与 notification_key_name 完全相同的电子邮件。所以如果你使用的是foo@gmail.com,你需要使用这个地址作为notification_key_name.
这是根据 greywolf82 的正确答案进行的总结。正确的代码应该遵循这些原则(错误处理等已被剥离):
///////////////////////////////////////////////////////////////////////////
// Working example on how to create a locally (client-side) managed //
// device group for Google Cloud Messaging. //
// //
// Thanks to greywolf82 for adding the final piece. //
// Please vote on his answer. Thank you! //
///////////////////////////////////////////////////////////////////////////
// Get token:
String account = ACCOUNT_NAME; // E.g. "johndoe@gmail.com"
String scope = "audience:server:client_id:" + WEB_APPLICATION_CLIENT_ID;
String idToken = GoogleAuthUtil.getToken(context, account, scope);
// Get registration id:
InstanceID instanceID = InstanceID.getInstance(this);
String registration_id = instanceID.getToken(
getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// Set up HTTP connection:
URL url = new URL("https://android.googleapis.com/gcm/googlenotification");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("project_id", NUMERICAL_PROJECT_ID);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.connect();
JSONObject requestBody = new JSONObject();
requestBody.put("operation", "add");
requestBody.put("notification_key_name", ACCOUNT_NAME); // You *must* use the email!
requestBody.put("registration_ids",
new JSONArray(Arrays.asList(new String[]{registration_id})));
requestBody.put("id_token", idToken);
// Submit request body
OutputStream os = connection.getOutputStream();
os.write(requestBody.toString().getBytes("UTF-8"));
os.close();
// connection.getResponseCode() is now 200 :-)
// Now read the server response contents from connection.getInputStream()
提交给 https://android.googleapis.com/gcm/notification 的 JSON 看起来像这样:
{
"operation": "add",
"notification_key_name": "johndoe@gmail.com",
"registration_ids": ["very long string here"],
"id_token": "another very long string"
}
回复内容为:
{
"notification_key": "your notification key is here --- voilá!"
}