为了防止 CDN 的 资源被盗刷而产生高额流量费;CDN 云厂商通常会给予我们简单的权限认证,避免未授权的文件被访问,进而避免资源被盗刷。我尝试了一下腾讯云基于时间戳的 Token 鉴权,还是挺巧妙和轻量的,这里分享给大家。
CDN 内容分发
首先科普一下 CDN (Content Delivery Network) : 我们都知道,服务器带宽资源有限,当多用户同时进行下载时,服务器可能承受不住压力。此外,由于网络环境的复杂性,不同地区访问服务器时的延迟会有所不同,甚至会在某些地区遇到较高的丢包率。
可以看到,我当前的网络环境,访问我的博客是非常稳定的。
因为我使用了 CDN (实际上是腾讯云 EdgeOne,相当于在 CDN 的边缘节点加速的情况下,加上了 WAF 功能): 通过在全球多个地点部署服务器, 将内容缓存到不同的节点服务器,用户访问网站内容时,优先从节点获取内容数据,从而减少了数据传输的距离和时间,提高了访问速度和网站的整体性能,减轻源站服务器的压力。
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 鉴权的方式完成,可以防止图片被盗问题。
细看 Case 2,腾讯云的 CDN 主要有两种鉴权方式: 远程鉴权 和 Token 鉴权(URL 时间戳鉴权)。
远程鉴权,需要使用云函数、云服务器等,设置自己的鉴权逻辑:
相比之下,我觉得 Token 鉴权方式更轻量,不需要额外的服务器;基于时间戳来绑定有效期,也可以防止盗刷:
还是挺有趣的。我们来看一下。
时间戳鉴权
终于到了“硬菜”了。前文说到,使用时间戳鉴权,就是改变 URL 的内容,内部添加 token 参数,当 token 无效(时间过期、篡改)时,拒绝访问。
细看时间戳鉴权。以腾讯云 CDN(包括 EdgeOne)为例,主要有四种鉴权签名计算方式:
- TypeA: 在请求的 URL 末尾附加一个计算出的签名,该签名通过将资源请求路径、时间戳、密钥以及一串随机字符串拼接起来,并对其进行MD5哈希运算得到。
- TypeB: 重新排列 URL 的目录结构,在域名和路径之间插入时间戳和计算出的签名,该签名通过将资源请求路径、时间戳以及密钥拼接起来,并对其进行MD5哈希运算得到。
- TypeC: 同样重新排列 URL 的目录结构,与 TypeB不同的是, 时间戳使用十六进制表示。
- TypeD: 与 TypeA 类似,只是 末尾附加计算出来的签名同时,附带时间戳。计算签名使用密钥、资源请求路径和时间戳拼接起来,并对其进行MD5哈希运算得到。时间戳支持十进制和十六进制表示。
我们以 TypeD 为例,来演示一下时间戳鉴权。首先,在腾讯云的 CDN 或者 EdgeOne 管理控制台,并且联动了腾讯云的 COS 存储桶:
配置当前的鉴权方式为 TypeD,然后配置密钥和过期时间,如下图所示:
可以看到,主要设置了:
- URL Path: URL 路径。我这里使用即存储桶的路径,比如:
/private/1.jpg
。我这里使用正则匹配。 - 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:
npm install js-md5
根据 TokenD 的计算方式,我们可以写一个 JavaScript 的函数,来计算签名和时间戳:
// 获取 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 鉴权生成
当然,为了更好的 Demo ,我实际上还有代码实现其他几种方式,比如:TypeA、TypeB、TypeC、TypeD,感兴趣的可以查看源码。
END
好了,关于腾讯云 CDN 的 Token 验证内容,就介绍到这里啦。感谢你的阅读。如果觉得文章对你有点帮助,记得分享给身边的小伙伴哦。其实 CDN 的扩展性挺高的,对网站的体验改善也非常不错,小伙伴们可以多尝试,多体验。
最后,如果你觉得本篇教程对你有帮助,迎加入我们的开发者交流群: 812198734 ,一起交流学习,共同进步。