动态权限附件体系构建与CDN分发
摘要:本文介绍了“载尔手记”系统中动态权限附件体系的构建,旨在安全管理公开和私有文档的图片访问。系统通过固定链接中转图片访问,结合后端鉴权和CDN签名校验实现权限控制。为优化性能,采用302重定向缓存减少重复请求,并自动压缩图片为WEBP格式以提升加载效率,确保图片资源的安全与高效访问。
背景
在开发“载尔手记”这个系统之前,我曾经想过要怎么存图片?文章通过markdown存储,按照一般的逻辑,上传到一个公开的图床,返回一个永久图片链接。
但是本系统在设计之初,不只有公开的博客文章,还存在私有的文档、备忘录等模块。如果上传到公开图床,就没法做到区分权限,存在泄露风险。
所以就引出了如何构建一个动态权限附件体系。
鉴权与签名校验
首先,在文章编辑器上传图片时,不能直接写入图片的源地址,而应该通过一个其他的固定链接来中转。
所以,本系统文章编辑器上传图片后,会生成https://api.zyaer.com/attachments/eeca359d/view这样的能唯一指定图片的链接,后续称为“固定链接”。
这个链接实际上是一个 API 接口,当访问这个接口时,首先需要经过第一层“鉴权”。在这一步,只有成功登录过的管理员,会写入一份 token 到 cookie 中,并随请求发送到后端服务器。在校验通过后,才会实际302重定向到图片源地址。访客访问私有文章的图片会被拦截并返回403无权限。
而图片源地址,则需要通过 CDN 来加速,同时配合所在厂商边缘计算能力,做一层签名校验。比如 Cloudflare + Worker,或者 阿里云 ESA + 边缘函数,之类的。而我则使用的是腾讯云 EO + 规则引擎的 Token 鉴权1。
签名校验本质上,是通过双方共同持有的秘钥,对资源进行签名,保证资源被修改后,能识别出来。而在该系统的应用中,则是给图片的源地址加上一把存在有效期的钥匙,只有持有这把钥匙,才能通过 CDN 厂商的验证,拿到图片文件,否则则拒绝访问。
只有通过固定链接的后端鉴权,签发一份对特定文件xx小时有效的签名,再拿签名附带上图片源地址,在有效期内才能访问到图片。
重定向缓存
运行一段时间后发现,重复访问同一张图片的时候,每次加载,都需要访问 API 获取新的签名,然后重定向时 URL 附带的签名与之前的不一致,这在浏览器看来,是不同的图片,就会触发图片的重新下载,而这是非必要的。
而要优化这点,其实有几个方向可以解决:
- 后端 API 服务器缓存签名。
- 引入 Service Worker 对固定链接缓存。
- 缓存 302 重定向关系。
每项都有优劣势,第1、2项会分别提高后端、前端的复杂度。目前发现最简单的是第3项。
而浏览器一般不会缓存302请求,所以需要在固定链接的重定向响应体中,手动添加Cache-Control字段。比如Cache-Control: max-age=3480, must-revalidate, private,则表示缓存最多58分钟,缓存过期必须重新请求,且只能浏览器缓存而中间节点不缓存。需要注意的是,缓存时间适当小于签名的有效期,避免临界时间图片失效。
图片压缩优化
在写文章时,经常截屏后直接复制到文章编辑器里,直接就上传到系统中。但是截屏的图片原图是没经过压缩的,通常大小会很大,一般几M都是常见的。
如果每次都本地压缩,再上传,步骤有点麻烦了,还会打断思路。在之前本地编辑 markdown 时,可以用如 PicList 等图床管理软件,直接在上传图床前自动压缩,但不适用于当前系统。
所以在系统中引入后台图片压缩模块,把 PNG 等格式的原图压缩成 WEBP 图片。得益于固定链接的302重定向设计,在后端 API 服务器鉴权后,可以根据请求头的Accept字段判断浏览器支持的图片格式,当存在image/webp格式时,直接重定向到压缩后的 WEBP 图片,否则重定向到原始图片。
后续随着 AVIF 格式图片的普及,也可以同时压缩 AVIF 图片,按照 AVIF -> WEBP -> JPG -> PNG 这样的顺序做格式退避,同时 markdown 文章里的图片链接不需要改动。
总结
综上,动态权限附件体系构建完成,总结流程如下:
渲染中...