消息通知服务可以将声网各业务下的事件,以 HTTPS 请求的形式通知到你的服务器。消息通知服务已集成旁路推流、输入在线媒体流和云端录制业务下的一些事件。你可以向声网提供相关的配置信息来开通该服务。
如果你的网络环境部署了防火墙,请将声网消息通知服务器的 IP 地址添加到白名单,以正常使用声网消息通知服务。详见查询消息通知服务器的 IP 地址。
你需要联系技术支持开通消息通知服务,并提供以下配置信息:
配置消息通知服务后,你需要注意以下事项:
secret
。secret
是字符串类型的密钥,可以用来生成签名,你需要保存这个密钥以备后续验签,详见验证签名。消息通知回调以 HTTPS POST 请求发送给你的服务器,请求的 body
格式为 JSON。字符编码为 UTF-8。
消息通知回调的 header
中包含以下字段:
字段名 | 值 |
---|---|
Content-Type |
application/json |
Agora-Signature |
声网用密钥(secret )和 HMAC/SHA1 算法生成的签名值。你需要使用密钥(secret )和 HMAC/SHA1 算法来验证该签名值。详见验证签名。 |
Agora-Signature-V2 |
声网用密钥(secret )和 HMAC/SHA256 算法生成的签名值。你需要使用密钥(secret )和 HMAC/SHA256 算法来验证该签名值。 详见验证签名。 |
消息通知回调的 body
中包含以下字段:
字段名 | 类型 | 含义 |
---|---|---|
noticeId |
String | 通知 ID,标识来自业务服务器的一次事件通知。 |
productId |
Number | 业务 ID: 2 :Push Stream to CDN 3.0(旁路推流 - 客户端 SDK API)3 :Cloud Recording and Extension Service(云端录制及扩展服务、云端转码)4 :Cloud Player(输入在线媒体流)5 :RTMP Converter (旁路推流 - 服务端 RESTful API) |
eventType |
Number | 通知的事件类型: |
notifyMs |
Number | 消息通知服务器向你的服务发出回调请求的 Unix 时间戳,单位为毫秒。 Note:通知重试时会更新该时间。 |
payload |
JSON Object | 事件内容: |
我们系统直接生成了密钥(secret
),并通过 HMAC/SHA1 和 HMAC/SHA256 算法最终生成 Agora-Signature
和 Agora-Signature-V2
签名。
你可以使用已保存的密钥(secret
),选用 HMAC/SHA1 或 HMAC/SHA256 算法计算签名值,以验证 Agora-Signature
或 Agora-Signature-V2
。
验证签名时你可以参考以下代码:
#!/usr/bin/env python2
# !-*- coding: utf-8 -*-
import hashlib
import hmac
# 拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 request_body 是反序列化之前的 binary byte array,不是反序列化之后的 dictionary
request_body = '{"eventMs":1560408533119,"eventType":10,"noticeId":"4eb720f0-8da7-11e9-a43e-53f411c2761f","notifyMs":1560408533119,"payload":{"a":"1","b":2},"productId":1}'
secret = 'secret'
signature = hmac.new(secret, request_body, hashlib.sha1).hexdigest()
print(signature) # 033c62f40f687675f17f0f41f91a40c71c0f134c
#!/usr/bin/env python2
# !-*- coding: utf-8 -*-
import hashlib
import hmac
# 拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 request_body 是反序列化之前的 binary byte array,不是反序列化之后的 dictionary
request_body = '{"eventMs":1560408533119,"eventType":10,"noticeId":"4eb720f0-8da7-11e9-a43e-53f411c2761f","notifyMs":1560408533119,"payload":{"a":"1","b":2},"productId":1}'
secret = 'secret'
signature2 = hmac.new(secret, request_body, hashlib.sha256).hexdigest()
print(signature2) # 6d3320c60b11101395b7fc8f9068748808a0aa1bfa064438e39d1bc2c7d74d99
const crypto = require('crypto')
// 拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 requestBody 是反序列化之前的 binary byte array,不是反序列化之后的 object
const requestBody = '{"eventMs":1560408533119,"eventType":10,"noticeId":"4eb720f0-8da7-11e9-a43e-53f411c2761f","notifyMs":1560408533119,"payload":{"a":"1","b":2},"productId":1}'
const secret = 'secret'
const signature = crypto.createHmac('sha1', secret).update(requestBody, 'utf8').digest('hex')
console.log(signature) // 033c62f40f687675f17f0f41f91a40c71c0f134c
const crypto = require('crypto')
// 拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 requestBody 是反序列化之前的 binary byte array,不是反序列化之后的 object
const requestBody = '{"eventMs":1560408533119,"eventType":10,"noticeId":"4eb720f0-8da7-11e9-a43e-53f411c2761f","notifyMs":1560408533119,"payload":{"a":"1","b":2},"productId":1}'
const secret = 'secret'
const signature2 = crypto.createHmac('sha256', secret).update(requestBody, 'utf8').digest('hex')
console.log(signature2) // 6d3320c60b11101395b7fc8f9068748808a0aa1bfa064438e39d1bc2c7d74d99
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class HmacSha {
// 将加密后的字节数组转换成字符串
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
//HMAC/SHA1 加密,返回加密后的字符串
public static String hmacSha1(String message, String secret) {
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(
"utf-8"), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("utf-8"));
return bytesToHex(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 request_body 是反序列化之前的 binary byte array,不是反序列化之后的 object
String request_body = "{\"eventMs\":1560408533119,\"eventType\":10,\"noticeId\":\"4eb720f0-8da7-11e9-a43e-53f411c2761f\",\"notifyMs\":1560408533119,\"payload\":{\"a\":\"1\",\"b\":2},\"productId\":1}";
String secret = "secret";
System.out.println(hmacSha1(request_body, secret)); //033c62f40f687675f17f0f41f91a40c71c0f134c
}
}
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class HmacSha {
// 将加密后的字节数组转换成字符串
public static String bytesToHex(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString();
}
//HMAC/SHA256 加密,返回加密后的字符串
public static String hmacSha256(String message, String secret) {
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(
"utf-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("utf-8"));
return bytesToHex(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 request_body 是反序列化之前的 binary byte array,不是反序列化之后的 object
String request_body = "{\"eventMs\":1560408533119,\"eventType\":10,\"noticeId\":\"4eb720f0-8da7-11e9-a43e-53f411c2761f\",\"notifyMs\":1560408533119,\"payload\":{\"a\":\"1\",\"b\":2},\"productId\":1}";
String secret = "secret";
System.out.println(hmacSha256(request_body, secret)); //033c62f40f687675f17f0f41f91a40c71c0f134c
}
}
<?php
function assertEqual($expect, $actual)
{
if ($expect != $actual) {
echo("\n assert failed");
echo("\n expect:\n " . $expect);
echo("\n actual:\n " . $actual);
echo("\n");
} else {
echo("assert ok\n");
echo("\n");
}
}
// 拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 requestBody 是反序列化之前的 binary byte array,不是反序列化之后的 object
$request_body = '{"eventMs":1560408533119,"eventType":10,"noticeId":"4eb720f0-8da7-11e9-a43e-53f411c2761f","notifyMs":1560408533119,"payload":{"a":"1","b":2},"productId":1}';
$secret = 'secret';
// 请求 header 中 Agora-Signature 的值
$sha1 = '033c62f40f687675f17f0f41f91a40c71c0f134c';
$res1 = (hash_hmac('sha1', $request_body, $secret));
assertEqual($res1, $sha1);
?>
<?php
function assertEqual($expect, $actual)
{
if ($expect != $actual) {
echo("\n assert failed");
echo("\n expect:\n " . $expect);
echo("\n actual:\n " . $actual);
echo("\n");
} else {
echo("assert ok\n");
echo("\n");
}
}
// 拿到消息通知的 raw request body 并对其计算签名,也就是说下面代码中的 requestBody 是反序列化之前的 binary byte array,不是反序列化之后的 object
$request_body = '{"eventMs":1560408533119,"eventType":10,"noticeId":"4eb720f0-8da7-11e9-a43e-53f411c2761f","notifyMs":1560408533119,"payload":{"a":"1","b":2},"productId":1}';
$secret = 'secret';
// 请求 header 中 Agora-Signature-V2 的值
$sha256 = '6d3320c60b11101395b7fc8f9068748808a0aa1bfa064438e39d1bc2c7d74d99';
$res2 = (hash_hmac('sha256', $request_body, $secret));
assertEqual($res2, $sha256);
?>
声网会动态调整消息通知服务器的 IP 地址,周期为 24 小时。你可以通过 RESTful API 查询声网消息通知服务器当前的 IP 地址。
https://api.agora.io/v1/ncs/ip
Authorization
:你需要使用声网提供的客户 ID和客户密钥,以及 Base64 算法生成一个凭证,并填入 HTTP 请求头部的 Authorization
字段。详见实现 HTTP 基本认证。
该 API 无包体参数。
请求成功时,响应包体的示例如下:
{
"data": {
"service": {
"hosts": [
{
"primaryIP": "xxx.xxx.xxx.xxx"
},
{
"primaryIP": "xxx.xxx.xxx.xxx"
}
]
}
}
}
字段含义:
primaryIP
:声网消息通知服务器的 IP 地址。收到响应时,你仅需关心该字段,并将 IP 地址(或 IP 地址列表)添加到白名单中。