有趣且轻量的 CDN URL 鉴权方式: 时间戳 Token 鉴权

本文最后更新于 2025年1月16日 凌晨

为了防止 CDN 的 资源被盗刷而产生高额流量费;CDN 云厂商通常会给予我们简单的权限认证,避免未授权的文件被访问,进而避免资源被盗刷。我尝试了一下腾讯云基于时间戳的 Token 鉴权,还是挺巧妙和轻量的,这里分享给大家。

分享一下

CDN 内容分发

首先科普一下 CDN (Content Delivery Network) : 我们都知道,服务器带宽资源有限,当多用户同时进行下载时,服务器可能承受不住压力。此外,由于网络环境的复杂性,不同地区访问服务器时的延迟会有所不同,甚至会在某些地区遇到较高的丢包率。

博客没有丢包情况

可以看到,我当前的网络环境,访问我的博客是非常稳定的。

因为我使用了 CDN (实际上是腾讯云 EdgeOne,相当于在 CDN 的边缘节点加速的情况下,加上了 WAF 功能): 通过在全球多个地点部署服务器,将内容缓存到不同的节点服务器,用户访问网站内容时,优先从节点获取内容数据,从而减少了数据传输的距离和时间,提高了访问速度和网站的整体性能,减轻源站服务器的压力。

graph LR
    User1[用户1] --> Node1[CDN 边缘节点]
    User2[用户2] --> Node2[CDN 边缘节点]
    User3[用户3] --> Node3[CDN 边缘节点]
    Node1 --> CDN[CDN 边缘/收敛节点]
    Node2 --> CDN[CDN 边缘/收敛节点]
    Node3 --> CDN[CDN 边缘/收敛节点]
    CDN --> Origin[源服务器]

    subgraph 全球边缘节点
        Node1
        Node2
        Node3
    end

CDN 本身其实就是一个缓存服务器,但是云厂商通常会提供一些额外的功能,比如:缓存、防盗链、限速、限流、鉴权等。我们文章介绍的是 CDN 的鉴权功能,就是衍生的功能,用来配合存储桶,非常合适。

对象存储桶

对象存储桶,是云厂商提供的一种存储服务,只是叫法可能不同,比如:Amazon S3、Google Cloud Storage、腾讯云 COS 和阿里云 OSS等。

它提供的是对象存储服务,即存储的是对象,每个对象包含一个键值对,键是对象名,值是对象内容(类似于 Redis 的 key-value)。如果只存储文件,那么使用存储桶是不错的选择。

是不是有疑问?

是不是有很多小伙伴问,为什么不直接用服务器存储? Nginx 开启一个目录映射,似乎也不是很麻烦,而且还可以鉴权。我最开始用存储桶也有这个想法,但是对比之下,主要有以下几个原因:

  • 成本更低、性能高效:存储桶通常比云服务器的存储成本更低。云服务器不仅需要支付存储费用,还需要支付计算资源的费用;并且云服务器通常需要配置额外的网络和安全措施,而存储桶则不需要。
  • 可扩展、高可用:存储桶提供了几乎无限的存储空间,可以轻松扩展,甚至是扩展到 TB 级别;而云服务器通常需要配置额外的网络和安全措施,以支持高可用性和扩展性。

如果你使用 Serverless 服务,那么存储桶还可以作为持久化数据的存储中心。比如: Serverless 搭建 WordPress,网站图片和 HTML 等数据存储在存储桶,使用 CDN 配合域名给用户提供访问服务。

联动 CDN

实际上,我们使用存储桶,都会激活一个默认的域名,比如我在腾讯云上新建的存储桶:

一个腾讯云存储桶

存储桶通常是一个地区的数据,跨地区访问,我们通常会将存储桶和 云厂商的 CDN 联动,实现跨地区加速访问和公有访问资源的防盗刷、轻量鉴权。

举个例子:

  • Case 1: 我们的存储桶可能是上海的某个地区,我们上传 App 的更新包到其中;如果只使用存储通,所有用户都从存储桶所在的上海地区下载,那么下载速度可能就会比较慢。这个时候,我们可以将 CDN 节点部署在离用户更近的地方,比如北京、广州等地区,这样用户下载时,就会从就近的 CDN 节点下载,从而提高下载速度。
  • Case 2: 我们使用存储桶当作图床,肯定是公有读;但是为了防止存储桶被盗刷,我们可以设置私有读,使用 CDN 公有范围。最后,当前端渲染图片的时候,资源的加载走 CDN 配合 Token 鉴权的方式完成,可以防止图片被盗问题。
flowchart LR
    User([公网用户])
    subgraph Public["公有读私有写方案"]
        B1[存储桶]
    end
    subgraph Private["私有读、私有写+CDN方案"]
        B2[边缘节点缓存资源/回源存储桶]
        CDN[边缘节点]
        Auth[Token鉴权]
    end
    
    User -->|直接访问| B1
    User -->|携带Token| CDN
    CDN -->|鉴权| Auth
    Auth -->|验证通过| B2
    Auth -->|验证失败| X[拒绝访问]
    
    style Public fill:#e1f3d8
    style Private fill:#fbe5e1

细看 Case 2,腾讯云的 CDN 主要有两种鉴权方式:远程鉴权Token 鉴权(URL 时间戳鉴权)。

腾讯云 EdgeOne 使用函数实现远程鉴权

远程鉴权,需要使用云函数、云服务器等,设置自己的鉴权逻辑:

sequenceDiagram
    participant Client as 客户端
    participant CDN as 边缘节点
    participant AuthServer as 鉴权服务器

    Client->>CDN: 发送请求(包含鉴权参数)
    CDN->>AuthServer: 转发请求
    AuthServer-->>CDN: 返回鉴权结果
    CDN->>Client: 响应客户端(允许/拒绝访问)

相比之下,我觉得 Token 鉴权方式更轻量,不需要额外的服务器;基于时间戳来绑定有效期,也可以防止盗刷:

sequenceDiagram
    participant 客户端
    participant 边缘节点

    客户端->>边缘节点: 发起请求(携带签名和时间戳)
    边缘节点->>边缘节点: 解析请求中的签名和时间戳
    边缘节点->>边缘节点: 获取鉴权算法并计算预期签名
    边缘节点->>边缘节点: 对比预期签名和请求签名
    alt 校验通过
        边缘节点->>客户端: 返回节点缓存内容
    else 校验失败
        边缘节点->>客户端: 拒绝访问请求
    end

还是挺有趣的。我们来看一下。

挺有趣的

支持创作

制作教程不易,如果热心的小伙伴,想支持创作,可以加入我们的电圈(还可以解锁远程协助、好友位😃):

WebChart Recognise

志同道合的小伙伴也是知音难觅。

  • 开发者爱好群: 👉 如果你对云服务器、CDN、云数据库和Linux等云计算感兴趣,亦或者喜欢编程、设计、产品、运营等领域,欢迎加入我们的开发者爱好群,一起交流学习(目前可能就我一个人?🤔,毕竟才刚刚创建~)。
QQ_Group Recognise

当然,也欢迎在B站、YouTube或微信公众号上关注我们:

MintimateBlog Recognise

更多:

时间戳鉴权

终于到了“硬菜”了。前文说到,使用时间戳鉴权,就是改变 URL 的内容,内部添加 token 参数,当 token 无效(时间过期、篡改)时,拒绝访问。

细看时间戳鉴权。以腾讯云 CDN(包括 EdgeOne)为例,主要有四种鉴权签名计算方式:

  • TypeA: 在请求的 URL 末尾附加一个计算出的签名,该签名通过将资源请求路径、时间戳、密钥以及一串随机字符串拼接起来,并对其进行MD5哈希运算得到。
  • TypeB: 重新排列 URL 的目录结构,在域名和路径之间插入时间戳和计算出的签名,该签名通过将资源请求路径、时间戳以及密钥拼接起来,并对其进行MD5哈希运算得到。
  • TypeC: 同样重新排列 URL 的目录结构,与 TypeB不同的是,时间戳使用十六进制表示
  • TypeD: 与 TypeA 类似,只是末尾附加计算出来的签名同时,附带时间戳。计算签名使用密钥、资源请求路径和时间戳拼接起来,并对其进行MD5哈希运算得到。时间戳支持十进制和十六进制表示。

我们以 TypeD 为例,来演示一下时间戳鉴权。首先,在腾讯云的 CDN 或者 EdgeOne 管理控制台,并且联动了腾讯云的 COS 存储桶:

配置 EdgeOne 和 存储桶联动

配置当前的鉴权方式为 TypeD,然后配置密钥和过期时间,如下图所示:

配置时间戳鉴权

可以看到,主要设置了:

  1. URL Path: URL 路径。我这里使用即存储桶的路径,比如:/private/1.jpg。我这里使用正则匹配。
  2. Token 鉴权: Token 鉴权选择了 TypeD,并且设置了主/备密钥、鉴权加密串参数名称、鉴权时间戳参数名称、时间格式为十六进制以及过期时间。

最后的 URL 形式是将https://www.example.com/private/demo/demo.png计算为https://www.example.com/private/demo/demo.png?sign=99883f521c85d00dd0a39ac6854269b1&t=675efd21

当我们的 URL 发送到 CDN/EdgeOne 的 边缘节点 时,边缘节点 服务器解析出 URL 中的 时间戳参数 与 Token 鉴权 与当前时间进行比较:

  • 如果 时间戳参数 有效时长小于当前时间,则服务器判定过期失效,并返回 HTTP 403错误。
  • 如果 时间戳参数 有效时长大于当前时间,则使用 MD5 算法算出 md5hash 的值,再比较计算出来的 md5hash 值与 URL 中传入的 Token 鉴权 值,如果一致则放过,不一致则返回 HTTP 403错误。

那么?我们如何计算签名和时间戳呢?

代码实现

首先,我们需要安装一个 MD5 加密库;在实际的使用过程中,将原 URL 计算出 EdgeOne 目标 URL 过程应该是后端进行的,我这里为了方便,直接前端 JavaScript 实现。

为了实现 MD5 加密,方法很多,比如:crypto-js或者js-md5;然后,我们就可以使用 MD5 加密库来计算签名和时间戳了。我这里使用 js-md5:

1
npm install js-md5

根据 TokenD 的计算方式,我们可以写一个 JavaScript 的函数,来计算签名和时间戳:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 获取 URL 的路径等信息
function getPathFromUrl(fullUrl) {
// 创建一个 URL 对象
const urlObj = new URL(fullUrl);
// 移除查询字符串
urlObj.search = ''; // 或者使用 delete url.search;
// 如果你还需要移除hash部分
urlObj.hash = '';
return {
pureHost: `${urlObj.protocol}//${urlObj.hostname}`,
purePath: urlObj.pathname,
pureUrl: urlObj.toString()
}
}

export function makeTokenD(fullUrl, secretKey, sign = "sign",
tType="Dec",tKey = "t") {
/**
* 计算签名和时间戳
* @param fullUrl 请求的 URL
* @param secretKey 密钥
* @param sign 鉴权加密串参数名称
* @param tType 时间戳格式,Dec 或者 Hex
* @param tKey 时间戳参数名称
* @return {string} 包含签名和时间戳的 URL
* */
let host = getPathFromUrl(fullUrl);
// 鉴权加密串参数名称
const signStr = sign || "sign";
// 鉴权时间戳参数名称
const tStr = tKey || "t";
let timestampMilliseconds = Math.floor(Date.now() / 1000);
if (tType === "Hex") {
// 时间戳使用十六进制表示
timestampMilliseconds = parseInt(timestampMilliseconds).toString(16);
}
const token = md5(`${secretKey}${host.purePath}${timestampMilliseconds}`);
return `${host.pureUrl}?${signStr}=${token}&${tStr}=${timestampMilliseconds}`;
}

我们顺便也写一个前端,来测试一下:腾讯云 CDN / EO Token 鉴权生成

腾讯云 CDN / EO Token 鉴权生成

当然,为了更好的 Demo ,我实际上还有代码实现其他几种方式,比如:TypeA、TypeB、TypeC、TypeD,感兴趣的可以查看源码。

END

好了,关于腾讯云 CDN 的 Token 验证内容,就介绍到这里啦。感谢你的阅读。如果觉得文章对你有点帮助,记得分享给身边的小伙伴哦。其实 CDN 的扩展性挺高的,对网站的体验改善也非常不错,小伙伴们可以多尝试,多体验。

最后,如果你觉得本篇教程对你有帮助,迎加入我们的开发者交流群: 812198734 ,一起交流学习,共同进步。



有趣且轻量的 CDN URL 鉴权方式: 时间戳 Token 鉴权
https://www.mintimate.cn/2025/01/11/edgeOneWithCosToken/
作者
Mintimate
发布于
2025年1月11日
许可协议