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

签名计算

最近更新时间2024.04.07 11:46:38

首次发布时间2023.01.30 20:19:07

视频点播提供了 Referer 防盗链和 IP 黑白名单的功能对访客进行过滤。但是在某些情况下,Referer 和 IP 可以被伪造,容易造成站点资源被恶意盗用。如果您对于站点内容的安全性有很高的要求,可以采用 URL 鉴权的方式。本文为您介绍 URL 鉴权的流程、签名计算的原理、逻辑关系和多种语言的 Demo 示例等内容。

URL 鉴权原理

客户端在发送请求至服务端时,按照您设定的签名规则计算签名,并在请求中包含这个签名。服务端收到请求后,需要校验签名。只有在校验通过的情况下,才会响应客户端请求。您可以通过示例代码了解实现签名规则的逻辑,也可以了解在视频点播中配置访问 URL 的鉴权逻辑。

计算签名的参数

客户端在计算签名时,需要的参数如下表所示。

参数

说明

key

表示在视频点播控制台配置的密钥。可以使用主密钥或者备用密钥。

uri

表示请求的文件路径。路径以斜杠(/)开头,不包含域名。比如请求的完整路径为 https://www.example.com/volcano.png。则 uri 的值为 /volcano.png。如果路径包含中文字符,您需要对路径编码。

timestamp

表示 URL 过期时间,格式是十进制的 Unix 时间戳。示例:1644406401

rand

表示一个随机字符串。长度为 1-100 位。可以包含字母和数字。示例:2e1ca42a1bb248408fc9cf435e5af744

uid

表示用户 ID,暂时未使用。设置成 0 即可。

请求示例

客户端请求

  • 带签名请求:https://www.example.com/img/volcano.png?auth_key=1644406401-2e1ca42a1bb248408fc9cf435e5af744-0-54959c1ec3448bf8e992554476248fab
  • timestamp:2022-02-09 11:33:21 GMT。对应的十进制 Unix 时间戳是 1644406401。

鉴权参数

参数

示例值

key

abc123def456

uri

/img/volcano.png

rand

2e1ca42a1bb248408fc9cf435e5af744

uid

0

timestamp

1644406401

鉴权逻辑

  1. 计算鉴权签名
    1. 视频点播收到客户端的带签名请求后,计算签名。计算过程使用以下 A 类型的请求格式:

scheme://host/uri?auth_key=timestamp-rand-uid-md5(uri-timestamp-rand-uid-key)(&...)
基于该请求格式,使用以下公式计算签名:
md5(/img/volcano.png-1644406401-2e1ca42a1bb248408fc9cf435e5af744-0-abc123def456)
MD5 值在鉴权时会转化成小写进行比较。

  1. 视频点播将签名值加入原始请求。得到的请求如下:

https://www.example.com/img/volcano.png?auth_key=1644406401-2e1ca42a1bb248408fc9cf435e5af744-0-54959c1ec3448bf8e992554476248fab

  1. 验证请求合法性
    1. 判断该请求和客户端请求是否一致。本示例中两者一致。
    2. 判断请求是否过期。假设视频点播收到请求的时间是 2022-02-09 11:40:21 GMT。该时间大于 2022-02-09 11:33:21。本示例中请求未过期。

Demo 示例

为方便您快速进行签名计算,点播提供了多种语言的 Demo 示例。

package main

import (
        "crypto/md5"
        "encoding/hex"
        "fmt"
        "math/rand"
        "regexp"
        "strconv"
        "time"
)

func splitUrl(url string) []string {
        reg := regexp.MustCompile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
        return reg.FindStringSubmatch(url)
}

func getMd5(text string) string {
        hashByte := md5.Sum([]byte(text))
        return hex.EncodeToString(hashByte[:])
}

func getRandomString(length int) string {
        b := make([]byte, length)
        rand.Read(b)
        return fmt.Sprintf("%x", b)[:length]
}

func GenTypeAUrl(url string, key string, signName string, uid string, ts int64) string {
        params := splitUrl(url)
        scheme, host, path, args := params[1], params[2], params[3], params[4]
        randstr := getRandomString(10)
        text := fmt.Sprintf("%s-%d-%s-%s-%s", path, ts, randstr, uid, key)
        hash := getMd5(text)
        authArg := fmt.Sprintf("%s=%d-%s-%s-%s", signName, ts, randstr, uid, hash)
        if args == "" {
                return fmt.Sprintf("%s%s%s?%s", scheme, host, path, authArg)
        } else {
                return fmt.Sprintf("%s%s%s%s&%s", scheme, host, path, args, authArg)
        }
}

func GenTypeBUrl(url string, key string, ts int64) string {
        params := splitUrl(url)
        scheme, host, path, args := params[1], params[2], params[3], params[4]
        tsStr := time.Unix(ts, 0).Format("200601021504")
        text := fmt.Sprintf("%s%s%s", key, tsStr, path)
        hash := getMd5(text)
        return fmt.Sprintf("%s%s/%s/%s%s%s", scheme, host, tsStr, hash, path, args)
}

func GenTypeCUrl(url string, key string, ts int64) string {
        params := splitUrl(url)
        scheme, host, path, args := params[1], params[2], params[3], params[4]
        tsStr := strconv.FormatInt(ts, 16)
        text := fmt.Sprintf("%s%s%s", key, path, tsStr)
        hash := getMd5(text)
        return fmt.Sprintf("%s%s/%s/%s%s%s", scheme, host, hash, tsStr, path, args)
}

func GenTypeDUrl(url, key, signName, timeName string, ts int64, base int) string {
        params := splitUrl(url)
        scheme, host, path, args := params[1], params[2], params[3], params[4]
        tsStr := strconv.FormatInt(ts, base)
        text := fmt.Sprintf("%s%s%s", key, path, tsStr)
        hash := getMd5(text)
        authArg := fmt.Sprintf("%s=%s&%s=%s", signName, hash, timeName, tsStr)
        if args == "" {
                return fmt.Sprintf("%s%s%s?%s", scheme, host, path, authArg)
        } else {
                return fmt.Sprintf("%s%s%s%s&%s", scheme, host, path, args, authArg)
        }
}

// GenTypeEUrl Genrate signed url by custom rule(eg.:key+domain+uri+timestamp)
func GenTypeEUrl(url, key, signName, tsName string, ts int64, base int) string {
        params := splitUrl(url)
        scheme, domain, uri, args := params[1], params[2], params[3], params[4]
        tsStr := strconv.FormatInt(ts, base)
        text := fmt.Sprintf("%s%s%s%s", key, domain, uri, tsStr)
        hash := getMd5(text)
        authArg := fmt.Sprintf("%s=%s&%s=%s", signName, hash, tsName, tsStr)
        if args == "" {
                return fmt.Sprintf("%s%s%s?%s", scheme, domain, uri, authArg)
        } else {
                return fmt.Sprintf("%s%s%s%s&%s", scheme, domain, uri, args, authArg)
        }
}

func init() {
        rand.Seed(time.Now().UnixNano())
}

func main() {
        var url = "http://www.test.com/a.txt?a=b&c=d"
        var primaryKey = "primary123456"
        var signName = "auth_key"
        var timeName = "t"
        var uid = "0"
        var ts = time.Now().Unix() + 3600 //默认一小时过期

        typeAUrl := GenTypeAUrl(url, primaryKey, signName, uid, ts)
        typeBUrl := GenTypeBUrl(url, primaryKey, ts)
        typeCUrl := GenTypeCUrl(url, primaryKey, ts)
        typeDUrl := GenTypeDUrl(url, primaryKey, signName, timeName, ts, 10)
        typeEUrl := GenTypeEUrl(url, primaryKey, signName, timeName, ts, 10)

        fmt.Println("OriginUrl: ", url)
        fmt.Println("TypeA: ", typeAUrl)
        fmt.Println("TypeB: ", typeBUrl)
        fmt.Println("TypeC: ", typeCUrl)
        fmt.Println("TypeD: ", typeDUrl)
        fmt.Println("TypeE: ", typeEUrl)
}