最近更新时间:2023.08.21 20:45:17
首次发布时间:2022.06.15 15:10:53
视频点播支持在 HTTP/HTTPS 回调时增加特定签名头,以便回调消息接收服务端进行签名认证,用来防止非法或无效请求。本文为您介绍 HTTP 回调鉴权的参数、签名算法等内容。
PrivateKey
,那么回调时会携带所有鉴权相关内容,供回调消息接收服务端进行鉴权使用。也就是设置PrivateKey
不会影响原有功能,具体是否校验由您自行决定。PrivateKey
,不会受任何影响。在回调 HTTP 头部增加的具体鉴权参数如下所示。
字段 | 描述 |
---|---|
X-VOD-TIMESTAMP | UNIX 时间戳,整型正数,固定长度 10(单位:秒),1970年1月1日以来的秒数,表示回调请求发起时间。 |
X-VOD-SIGNATURE | 签名字符串,为 32位 MD5 值,详情请见签名算法。 |
X-VOD-SIGNATURE 的计算依赖如下字段所示。
字段 | 示例 | 描述 |
---|---|---|
回调 URL | https://www.example1.com/your/callback | 用户设置的回调地址。 |
X-VOD-TIMESTAMP | 1545675780 | UNIX 时间戳,整型正数,固定长度 10。1970年1月1日以来的秒数,表示回调请求发起时间。 |
PrivateKey | ABCDabcd1234 | 用户预设的签名 Key。 |
CallbackDataJsonBase64Encode | ewoiYSI6MSwKImIiOjIKfQ== | Base64Encode 处理后的回调数据。 注意 请不要去掉空格。 |
将上述四个字段按照上述顺序进行拼接,字段中间以竖线(|)分割,后计算 MD5 值,示例如下所示。
CallbackContent = base64Encode(CallbackDataJson) MD5Content = 回调URL|X-VOD-TIMESTAMP|PrivateKey|CallbackContent X-VOD-SIGNATURE = md5sum(MD5Content)
您在切换 PrivateKey 时,为保证回调鉴权不受影响,消息接收服务端需要兼容新旧两个 PrivateKey 的平滑切换,即在一段时间内兼容新旧两个 PrivateKey 的鉴权,由服务方完成。
推荐的操作流程如下所示。
PrivateKey
。说明
由于时间设置等问题,时间差值可能会存在误差,服务端可自行决定是否进行校验。
func Callback(r *http.Request) { timestamp := r.Header.Get("X-VOD-TIMESTAMP") //可以校验 X-VOD-TIMESTAMP 与当前时间,不能超过某个范围 tm, _ := strconv.ParseInt(timestamp, 10, 64) if time.Now().Unix() - tm > consts.MaxRequestTime { //超过某个范围验签不通过 } //请求 body 进行 base64 编码 body, _ := io.ReadAll(r.Body) callbackContent := base64.StdEncoding.EncodeToString(body) //拼接签算内容,竖线分割,规则为回调 URL|X-VOD-TIMESTAMP|PrivateKey|CallbackContent //因为修改 key 后配置生效有延迟,因此修改 key 时建议兼容两种 key 的配置,这里只给出一个 key 的计算结果 md5Content := fmt.Sprintf("%s|%s|%s|%s", url, timestamp, privateKey, callbackContent) //计算拼接完内容的 md5 hash := md5.New() hash.Write([]byte(md5Content)) sign := hex.EncodeToString(hash.Sum(nil)) //判断与 Header:X-VOD-SIGNATURE 是否一致 if sign == r.Header.Get("X-VOD-SIGNATURE") { //todo 验签通过 } else { //todo 验签不通过 } }
import hashlib import base64 def callback(): url = request.url content = str(base64.b64encode(request.data), 'utf-8') #header 大小写不敏感,需要用get获取 timestamp = request.headers.get('X-VOD-TIMESTAMP') #可以校验 X-VOD-TIMESTAMP 与当前时间,不能超过某个范围 #todo #验签规则,竖线分割,规则为回调 URL|X-VOD-TIMESTAMP|PrivateKey|CallbackContent #因为修改 key 后配置生效有延迟,因此修改 key 时建议兼容两种 key 的配置,这里只给出一个 key 的计算结果 private_key = 'privateKey' signature = hashlib.md5("{}|{}|{}|{}".format(url, timestamp, private_key, content).encode("utf-8")).hexdigest() if signature == request.headers.get('X-VOD-SIGNATURE'): #鉴权通过 else: #鉴权失败 json_data = {"code": 0, "message": "success"} return jsonify(json_data)
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class MyCallbackHandler { public String callback(HttpServletRequest request, HttpServletResponse response) { String url = request.getRequestURL().toString(); String content = new String(Base64.getEncoder().encode(getRequestBody(request)), StandardCharsets.UTF_8); //header 大小写不敏感,需要用 getHeader 获取 String timestamp = request.getHeader("X-VOD-TIMESTAMP"); //可以校验 X-VOD-TIMESTAMP 与当前时间,不能超过某个范围 //todo //验签规则,竖线分割,规则为回调 URL|X-VOD-TIMESTAMP|PrivateKey|CallbackContent //因为修改 key 后配置生效有延迟,因此修改 key 时建议兼容两种 key 的配置,这里只给出一个 key 的计算结果 String privateKey = "privateKey"; String signature = calculateSignature(url, timestamp, privateKey, content); if (signature.equals(request.getHeader("X-VOD-SIGNATURE"))) { //鉴权通过 } else { //鉴权失败 } String jsonResponse = "{\"code\": 0, \"message\": \"success\"}"; return jsonResponse; } private byte[] getRequestBody(HttpServletRequest request) { //获取请求体内容并转为字节数组 try (InputStream inputStream = request.getInputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } return outputStream.toByteArray(); } catch (IOException e) { e.printStackTrace(); return new byte[0]; } } private String calculateSignature(String url, String timestamp, String privateKey, String content) { String message = url + "|" + timestamp + "|" + privateKey + "|" + content; try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(message.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } } }
<?php function callback() { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://'; $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $content = base64_encode(file_get_contents('php://input')); //header 大小写不敏感,需要用 get 获取 $timestamp = $_SERVER['HTTP_X_VOD_TIMESTAMP']; //可以校验 X-VOD-TIMESTAMP 与当前时间,不能超过某个范围 //todo //验签规则,竖线分割,规则为回调 URL|X-VOD-TIMESTAMP|PrivateKey|CallbackContent //因为修改 key 后配置生效有延迟,因此修改 key 时建议兼容两种 key 的配置,这里只给出一个 key 的计算结果 $private_key = 'privateKey'; $signature = md5($url . '|' . $timestamp . '|' . $private_key . '|' . $content); if ($signature == $_SERVER['HTTP_X_VOD_SIGNATURE']) { //鉴权通过 } else { //鉴权失败 } $json_data = array("code" => 0, "message" => "success"); return json_encode($json_data); } ?>