数据推送
概述
数据推送是指在某个资源发生变化时,自动向订阅的应用发送变化相关的信息请求的功能。
配置
在开发者后台中,配置应用的订阅的地址、Token 和关注的资源。
- 推送地址:一个可访问的公网地址,推送的信息会通过 HTTP GET 请求发送到该地址。在正常的情况下,接口应返回 200 状态码。
- Token:一个字符串,用于参与验证推送请求的合法性。由开发者自行生成。
- 资源:开发者可以选择关注的资源,比如用户、权限等。
请求参数格式
请求方式:GET 请求参数:
参数名称 | 参数类型 | 参数说明 |
---|---|---|
nonce | string | 随机字符串,用于签名 |
timestamp | integer | Unix 时间戳 |
signature | string | 签名信息 |
op | string | 操作类型,可能的值 updated ,created ,deleted |
school_id | integer | 学校 ID |
identity | string | 资源标识 |
type | string | 操作类型, |
operated_at | string | 操作时间,格式为 2024-12-31 12:00:00 |
推送示例如下:
GET https://yourdomain.com/?nonce=9xmas123×tamp=1713153015&signature=xxxx&&op=updated&school_id=1&identity=1&operated_at=2024-01-01 00:00:00
请求认证
为保证推送请求的合法性,我们在请求中加入了签名信息,开发者可以通过验证签名信息来判断请求的合法性。
签名信息的生成方式如下:
- 将请求参数按照参数名的字典序排序
- 参数包括:nonce, timestamp, op, school_id, identity, operated_at
- 注意参数 school_id 和 timestamp 需要转换为整型
- 将参数编码成 JSON 字段串
- 使用 HMAC-SHA256 算法,使用开发者配置的 Token 作为密钥,对 json 字段串进行签名
- 将签名信息加入到请求参数中
签名示例:
预定义的 Token 值:87892dedaf483eeabed6c54e4335fbe5
收到请求如下:
https://yourdomain.com/?identity=1&nonce=bfcf312b&op=created&operated_at=2024-04-15%2014%3A25%3A32&school_id=0×tamp=1713162332&type=ping&signature=74b48b7a98c2fb8acbc99f41582390e98b535a4fa2e1b2fa33a1224aa8ff0220
校验签名方式如下:
PHP 版本示例
<?php
$token = '87892dedaf483eeabed6c54e4335fbe5';
$queryStr = 'identity=1&nonce=bfcf312b&op=created&operated_at=2024-04-15%2014%3A25%3A32&school_id=0×tamp=1713162332&type=ping&signature=74b48b7a98c2fb8acbc99f41582390e98b535a4fa2e1b2fa33a1224aa8ff0220';
parse_str($queryStr, $query);
$signature = $query['signature'];
unset($query['signature']);
$query['school_id'] = (int)$query['school_id'];
$query['timestamp'] = (int)$query['timestamp'];
ksort($query);
$json = json_encode($query);
var_dump($json);
$hash = hash_hmac('sha256', $json, $token);
if ($hash === $signature) {
echo '验证成功';
} else {
echo '验证失败';
}
// https://onlinephp.io/c/59c83d
Python 版本示例
import hashlib
import hmac
import json
from urllib.parse import parse_qs
token = '87892dedaf483eeabed6c54e4335fbe5'
query_str = 'identity=1&nonce=bfcf312b&op=created&operated_at=2024-04-15%2014%3A25%3A32&school_id=0×tamp=1713162332&type=ping&signature=74b48b7a98c2fb8acbc99f41582390e98b535a4fa2e1b2fa33a1224aa8ff0220'
query_dict = parse_qs(query_str, keep_blank_values=True)
query = {k: v[0] for k, v in query_dict.items()}
signature = query['signature']
del query['signature']
query['school_id'] = int(query['school_id'])
query['timestamp'] = int(query['timestamp'])
sorted_query = dict(sorted(query.items()))
json_data = json.dumps(sorted_query, ensure_ascii=False, separators=(',', ':'))
print(json_data)
hash_value = hmac.new(token.encode(), json_data.encode(), hashlib.sha256).hexdigest()
if hash_value == signature:
print('验证成功')
else:
print('验证失败')
// https://www.online-python.com/B8Gdh9CpRX
响应
推送请求的响应应返回 200 状态码,响应 Body 不作要求。