You need to enable JavaScript to run this app.
导航

HTTP 回调鉴权

最近更新时间2023.08.21 20:45:17

首次发布时间2022.06.15 15:10:53

视频点播支持在 HTTP/HTTPS 回调时增加特定签名头,以便回调消息接收服务端进行签名认证,用来防止非法或无效请求。本文为您介绍 HTTP 回调鉴权的参数、签名算法等内容。

注意事项

  • 推荐您开启回调鉴权。开启后,可防止非法或无效请求。
  • 如果设置了PrivateKey,那么回调时会携带所有鉴权相关内容,供回调消息接收服务端进行鉴权使用。也就是设置PrivateKey不会影响原有功能,具体是否校验由您自行决定。
  • 如果未设置PrivateKey,不会受任何影响。

鉴权参数

在回调 HTTP 头部增加的具体鉴权参数如下所示。

字段描述
X-VOD-TIMESTAMPUNIX 时间戳,整型正数,固定长度 10(单位:秒),1970年1月1日以来的秒数,表示回调请求发起时间。
X-VOD-SIGNATURE签名字符串,为 32位 MD5 值,详情请见签名算法

签名算法

X-VOD-SIGNATURE 的计算依赖如下字段所示。

字段示例描述
回调 URLhttps://www.example1.com/your/callback用户设置的回调地址。
X-VOD-TIMESTAMP1545675780UNIX 时间戳,整型正数,固定长度 10。1970年1月1日以来的秒数,表示回调请求发起时间。
PrivateKeyABCDabcd1234用户预设的签名 Key。

CallbackDataJsonBase64Encode

ewoiYSI6MSwKImIiOjIKfQ==

Base64Encode 处理后的回调数据。

注意

请不要去掉空格。

将上述四个字段按照上述顺序进行拼接,字段中间以竖线(|)分割,后计算 MD5 值,示例如下所示。

CallbackContent = base64Encode(CallbackDataJson)
MD5Content = 回调URL|X-VOD-TIMESTAMP|PrivateKey|CallbackContent
X-VOD-SIGNATURE = md5sum(MD5Content)

PrivateKey 切换

您在切换 PrivateKey 时,为保证回调鉴权不受影响,消息接收服务端需要兼容新旧两个 PrivateKey 的平滑切换,即在一段时间内兼容新旧两个 PrivateKey 的鉴权,由服务方完成。

推荐的操作流程如下所示。

  1. 您可自行定义新的 PrivateKey。
  2. 升级回调消息接收服务端完成后,兼容新、旧两个 PrivateKey 的鉴权。
  3. 在点播控制台,选择导航栏系统设置>回调设置,更新鉴权密钥PrivateKey
  4. 等待一段时间后,回调消息接收服务端去掉对原来 PrivateKey 的兼容。
  5. 切换完成。

回调消息接收服务端校验规则

  1. 回调消息接收端将接收到的回调 Json 进行 Base64 编码。
  2. 回调消息接收端将回调所设置的回调 URL、X-VOD-TIMESTAMP 取值、PrivateKey 字符串、Base64 编码后的 CallbackJson 拼接后,计算 MD5 值,获取加密字符串,再将加密字符串与 X-VOD-SIGNATURE 字段进行对比,如果不一致,那么请求是非法的。
  3. (可选)回调消息接收端获取当前时间,与回调请求所带的 X-VOD-TIMESTAMP 字段时间相减,如果超过服务端所设定的指定时间(如 8分钟,由服务端自行定义),则认为该请求无效。

说明

由于时间设置等问题,时间差值可能会存在误差,服务端可自行决定是否进行校验。

回调地址服务端 Demo 示例

  • 回调地址的服务端 Go SDK 的 Demo 示例如下所示。
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 验签不通过
   }
}
  • 回调地址的服务端 Python SDK 的 Demo 示例如下所示。
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)
  • 回调地址的服务端 Java SDK 的 Demo 示例如下所示。
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 SDK 的 Demo 示例如下所示。
<?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);
}

?>