增量数据推送(暂未实现)

概述

数据推送是指在某个资源发生变化时,自动向订阅的应用发送变化相关的信息请求的功能。

配置

在开发者后台中,配置应用的订阅的地址、Token 和关注的资源。

  1. 推送地址:一个可访问的公网地址,推送的信息会通过 HTTP POST 请求发送到该地址。在正常的情况下,接口应返回 200 状态码。
  2. Token:一个字符串,用于参与验证推送请求的合法性。由开发者自行生成。
  3. 资源:开发者可以选择关注的资源,比如用户、权限等。

请求参数格式

请求方式:POST

Header 参数

参数名称 参数类型 参数说明
X-Nonce string 随机字符串,用于签名
X-Timestamp integer Unix 时间戳
X-Signature string 签名信息
X-School-Id integer 学校 ID

Request Body

Content-Type: application/json

{
   "delivery_id": "202401010000000001",
   "resource": "user",
   "events": [
       {
           "op": "created|updated|deleted",
           "identity": 1,
           "timestamp": "2024-01-01 00:00:00"
       }
   ]
}
字段名称 字段类型 字段说明
delivery_id string 本次推送的唯一 ID,开发者可以根据该 ID 进行幂等处理
resource string 资源类型,用于标识本次推送所属的资源
events array 事件列表,每个元素代表一条事件
events.op string 操作类型,可能的值 updatedcreateddeleted
events.identity string 资源标识
events.timestamp string 操作时间,格式为 2024-12-31 12:00:00

推送示例如下:

POST https://yourdomain.com/
Content-Type: application/json
X-Nonce: 9xmas123
X-Timestamp: 1713153015
X-Signature: xxxx
X-School-Id: 1

{
  "delivery_id": "202401010000000001",
  "resource": "user",
  "events": [
    {
      "op": "updated",
      "identity": "1",
      "timestamp": "2024-01-01 00:00:00"
    },
    {
      "op": "created",
      "identity": "2",
      "timestamp": "2024-01-01 00:00:01"
    }
  ]
}

请求认证

为保证推送请求的合法性,我们在请求中加入了签名信息,开发者可以通过验证签名信息来判断请求的合法性。

签名信息的生成方式如下:

  1. 创建一个字典,写入 nonce(取自 X-Nonce)和 timestamp(取自 X-Timestamp,转换为整型)
  2. 将 Request Body 解析为字典后,合并(merge)进上述字典
  3. 对合并后的字典递归地按 key 字典序排序(ksort)
  4. 将排序后的字典编码成 JSON 字符串
  5. 使用 HMAC-SHA256 算法,以开发者配置的 Token 为密钥,对 JSON 字符串进行签名
  6. 将签名结果以十六进制字符串形式填入 X-Signature Header

签名示例:

预定义的 Token 值:87892dedaf483eeabed6c54e4335fbe5 收到请求如下:

POST https://yourdomain.com/
Content-Type: application/json
X-Nonce: bfcf312b
X-Timestamp: 1713162332
X-Signature: 74b48b7a98c2fb8acbc99f41582390e98b535a4fa2e1b2fa33a1224aa8ff0220
X-School-Id: 1

{"delivery_id":"202404150000000001","resource":"user","events":[{"op":"created","identity":"1","timestamp":"2024-04-15 14:25:32"}]}

校验签名方式如下:

PHP 版本示例

<?php
$token = '87892dedaf483eeabed6c54e4335fbe5';

$nonce     = $_SERVER['HTTP_X_NONCE'];
$timestamp = (int)$_SERVER['HTTP_X_TIMESTAMP'];
$signature = $_SERVER['HTTP_X_SIGNATURE'];
$rawBody   = file_get_contents('php://input');

$params = [
    'nonce'     => $nonce,
    'timestamp' => $timestamp,
];
$body = json_decode($rawBody, true);
$merged = array_merge($params, $body);

// 递归 ksort
function ksort_recursive(array &$arr): void {
    ksort($arr);
    foreach ($arr as &$v) {
        if (is_array($v)) ksort_recursive($v);
    }
}
ksort_recursive($merged);

$json = json_encode($merged, JSON_UNESCAPED_UNICODE);
$hash = hash_hmac('sha256', $json, $token);
if ($hash === $signature) {
    echo '验证成功';
} else {
    echo '验证失败';
}

Python 版本示例

import hashlib
import hmac
import json

token = '87892dedaf483eeabed6c54e4335fbe5'

# 从请求 Header 中获取
nonce     = 'bfcf312b'
timestamp = 1713162332
signature = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# 从请求 Body 中读取并解析
raw_body  = '{"delivery_id":"202404150000000001","resource":"user","events":[{"op":"created","identity":"1","timestamp":"2024-04-15 14:25:32"}]}'
body = json.loads(raw_body)

params = {
    'nonce': nonce,
    'timestamp': timestamp,
}
merged = {**params, **body}

def ksort_recursive(obj):
    if isinstance(obj, dict):
        return {k: ksort_recursive(v) for k, v in sorted(obj.items())}
    if isinstance(obj, list):
        return [ksort_recursive(i) for i in obj]
    return obj

sorted_data = ksort_recursive(merged)
json_data = json.dumps(sorted_data, ensure_ascii=False, separators=(',', ':'))

hash_value = hmac.new(token.encode(), json_data.encode(), hashlib.sha256).hexdigest()
if hash_value == signature:
    print('验证成功')
else:
    print('验证失败')

响应

推送请求的响应应返回 200 状态码,响应 Body 不作要求。

results matching ""

    No results matching ""