</>开发学习笔记
← 返回全部笔记
后端

不装 SDK,自己写对象存储的请求签名

2026-11-09阅读约 2 分钟

最近把应用里用户上传的图片从数据库搬到了对象存储。服务端只需要五个操作:上传、下载、删除、列出文件、生成一个限时的下载地址。官方 SDK 功能很全,但为五个接口引入一整个依赖不太划算,于是决定自己写请求签名。

签名本身不难

这家对象存储的签名算法是 HMAC-SHA1,大致四步:

KeyTime      = "开始秒;结束秒"
SignKey      = HMAC-SHA1(SecretKey, KeyTime)
HttpString   = 方法小写 \n 路径 \n 查询参数 \n 请求头 \n
StringToSign = "sha1" \n KeyTime \n SHA1(HttpString) \n
Signature    = HMAC-SHA1(SignKey, StringToSign)

容易出错的地方都在细节里:方法名要转小写;查询参数和请求头要按小写键名排序,值要做 URL 编码,而且比 encodeURIComponent 多编码 !'()* 这几个字符;起始时间往前留一分钟,容忍服务器之间的时钟误差。

难的是证明它写对了

为了让测试不依赖网络,我在本机写了一个假的对象存储服务,收到请求后按同样的算法重新算一遍签名来校验。测试全绿之后,我故意把「方法名转小写」这一步删掉,结果测试照样全绿。

原因很简单:假服务和客户端用的是同一个签名函数,两边错得一模一样,当然对得上。用自己验证自己,等于没有验证。

用真服务的错误码来区分

手边正好有一把权限很小的钥匙:只能上传,不能读也不能删。拿它去请求真服务,能看到两种不同的拒绝:

于是上传、读取、删除、生成下载地址各试一次:上传成功,其余三个都回 AccessDenied 而不是签名错误,说明四种请求的签名都是对的。再故意把密钥改错一个字符,确认会得到 SignatureDoesNotMatch,证明这个判断方法本身是灵的。

把验证过的结果钉住

真服务验证通过后,用固定的时间和假密钥算出几串签名,原样写进测试作为「标准答案」。以后谁改了签名代码,这几条测试就会变红。再故意删掉转小写那一步,这次只有这几条标准答案测试变红,假服务那一组仍然是绿的,这正是需要它们的原因。

顺便记两个真服务上的意外