阿里云 V4 签名实战(第1篇)· Python:手写 OSS PutObject V4 签名(附完整工程)

简介: 为什么 SDK 之外还要手写签名?本篇用约 130 行纯 Python 实现阿里云 OSS V4 签名(OSS4-HMAC-SHA256)完成 PutObject 上传:四步流程图解、规范化请求与派生密钥链逐段讲解,配套完整工程可直接导入运行,并用官方文档示例做离线测试向量证明实现逐字节正确,附 7 条签名踩坑清单。

从本篇起,专栏进入「V4 签名实战」系列:按 Python → Java → C++ 轮流手写阿里云两种 V4 签名,每篇配套一个可直接导入运行的完整工程(装好依赖、替换 AK/SK 即可跑)。本篇:Python 手写 OSS V4 签名(OSS4-HMAC-SHA256) 完成 PutObject 上传,并用官方文档示例离线证明实现正确。

配套完整工程(可直接导入编辑器运行):
https://github.com/zhoubiao188/aliyun-v4-signature-examples/tree/main/python-oss-putobject-v4

一、为什么要手写签名?

用 SDK 一行代码就完成上传,为什么还要手写签名?三个真实理由:

  1. 排查线上 403:SignatureDoesNotMatch 是 OSS 高频问题,不看懂签名串构造,你连日志都读不明白;
  2. SDK 覆盖不到的场景:自研网关、边缘设备、特殊语言运行时里没有官方 SDK,签名是你唯一能自己攒出来的东西;
  3. 理解云上鉴权的本质:V4 签名与 AWS SigV4 同源,学会一套,看懂一片。

二、OSS V4 签名原理:四步图解

① CanonicalRequest(规范化请求)
   = HTTP方法 \n 规范化URI \n 规范化Query \n 规范化Headers \n AdditionalHeaders \n UNSIGNED-PAYLOAD
② StringToSign(待签名字符串)
   = "OSS4-HMAC-SHA256" \n <x-oss-date> \n <日期>/<地域>/oss/aliyun_v4_request \n SHA256hex(①)
③ SigningKey(派生密钥,HMAC 链)
   kDate    = HMAC-SHA256( "aliyun_v4" + SK , 日期 )
   kRegion  = HMAC-SHA256( kDate , 地域 )
   kService = HMAC-SHA256( kRegion , "oss" )
   kSigning = HMAC-SHA256( kService , "aliyun_v4_request" )
④ Signature 与请求头
   Signature    = HEX( HMAC-SHA256( kSigning , ② ) )
   Authorization: OSS4-HMAC-SHA256 Credential=AK/<scope>,Signature=<④>

几个容易被忽略的规范细节(本文实现全部与官方文档逐字节对齐):

  • x-oss-content-sha256 必须存在且值为 UNSIGNED-PAYLOAD(当前 V4 只支持此值,不做真实负载哈希),规范化请求最后一段同样写 UNSIGNED-PAYLOAD;
  • 参与签名的头:x-oss-content-sha256 必签;Content-Type、Content-MD5、所有 x-oss-* 头存在即参与;其他普通头(如 content-disposition)想参与签名,必须在 Authorization 的 AdditionalHeaders 中声明;
  • 规范化 Headers:头名小写、按字典序排序、值去首尾空白,每个头以 \n 结尾;
  • URI 编码:仅 A-Za-z0-9-._~ 不编码(空格是 %20 不是 +),对象键路径保留 /;
  • x-oss-date 必须是 UTC ISO8601 格式,如 20250411T064124Z;Scope 中的日期取它的前 8 位;
  • 与 AWS SigV4 不同:OSS V4 默认不签 host 头,也无密钥派生中的"服务名+终止串"之外的额外段,整体更简洁。

三、工程结构与运行方式

python-oss-putobject-v4/
├── main.py             # 签名实现 + PutObject 主流程(约 130 行)
├── test_signature.py   # 用官方文档示例离线验证(无需网络和真实 AK)
└── requirements.txt    # 仅 requests
pip install -r requirements.txt
export ALIBABA_CLOUD_ACCESS_KEY_ID=<你的AK>
export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<你的SK>
export OSS_BUCKET=<你的Bucket>
python3 test_signature.py    # ① 先验证实现正确性
python3 main.py --dry-run    # ② 打印签名全过程,不发请求
python3 main.py              # ③ 真实上传,预期 HTTP 200 + ETag

四、核心代码逐段讲解

派生密钥链(③)——注意每一步都是"上一步的输出做 key,固定字符串做 message":

def build_signing_key(secret: str, date: str, region: str) -> bytes:
    k_date    = hmac_sha256(("aliyun_v4" + secret).encode(), date)
    k_region  = hmac_sha256(k_date, region)
    k_service = hmac_sha256(k_region, "oss")
    return      hmac_sha256(k_service, "aliyun_v4_request")

规范化请求(①)——五段拼接,头名小写排序、每行自带换行:

names = sorted(headers)
canonical_headers = "".join(f"{n}:{str(headers[n]).strip()}\n" for n in names)
canonical_query = "&".join(f"{uri_encode(k, False)}={uri_encode(str(v), False)}"
                           for k, v in sorted(query_items))
canonical_request = "\n".join(
    [method, canonical_uri, canonical_query,
     canonical_headers, additional_headers, "UNSIGNED-PAYLOAD"])

待签串与签名(②④):

scope = f"{x_oss_date[:8]}/{region}/oss/aliyun_v4_request"
string_to_sign = "\n".join(
    ["OSS4-HMAC-SHA256", x_oss_date, scope, sha256_hex(canonical_request.encode())])
signing_key = build_signing_key(sk, x_oss_date[:8], region)
signature = hmac.new(signing_key, string_to_sign.encode(), hashlib.sha256).hexdigest()
authorization = (f"OSS4-HMAC-SHA256 Credential={ak}/{scope},"
                 f"Signature={signature}")

发起请求:把 Authorization、x-oss-date、x-oss-content-sha256: UNSIGNED-PAYLOAD、content-type 放进请求头,requests.put(url, data=body) 即可(requests 自动附加的 User-Agent 等头不参与签名,无需处理)。

五、用官方示例证明实现正确(本文最硬核的部分)

手写签名最大的痛点是"我不知道我对没对"。解法:把官方文档《在Header中包含V4签名》里的完整示例做成测试向量(test_signature.py),它的期望值全部来自官方文档原文:

[PASS] CanonicalRequest 的 SHA256(官方示例值)
       c46d96390bdbc2d739ac9363293ae9d710b14e48081fcb22cd8ad54b63136eca
[PASS] 用文档 SigningKey 验证 StringToSign(HMAC 结果 = 官方签名)
       053edbf550ebd239b32a9cdfd93b0b2b3f2d223083aa61f75e9ac16856d61f23
[PASS] 本工程 SigningKey(官方 SDK 公式,仅供对照)

第一条通过,说明规范化请求的每一处换行、排序、编码都与官方逐字节一致——这是手写签名最容易翻车的地方;第二条通过,说明待签串构造正确(用文档给的 SigningKey 直接 HMAC 我的 StringToSign,得到与文档完全相同的最终签名)。

一个意料之外的发现:文档展示的 SigningKey 中间值,用文档标注的 SK/日期/地域按文档公式算不出来(文档也自我声明"仅供参考,实际以自己计算的结果为准")。我对照了官方 Python SDK(oss2/auth.py 的 __get_signing_key)源码,派生链与本文实现逐行一致——所以以 SDK 公式为准,别去背文档那个中间值。

六、踩坑清单

  1. 头名大小写与排序:规范化头必须小写 + 字典序,X-Oss-Date 和 x-oss-date 是两个东西;
  2. "存在即参与"的头漏签:请求里带了 content-type 却没放进规范化请求,必得 SignatureDoesNotMatch;反之,没带的头不要乱签;
  3. 时间必须 UTC:x-oss-date 用本地时间或格式不对,轻则签名不匹配,重则 RequestTimeTooSkewed;本机时钟漂移也会 403;
  4. UNSIGNED-PAYLOAD 是当前唯一支持的取值:照搬 AWS SigV4 的"真实负载 SHA256"写法反而会失败;
  5. URI 编码用 %20 不用 +:urllib.parse.quote 而不是 quote_plus,保留字符只有 A-Za-z0-9-._~;
  6. virtual-hosted 与 path 风格:bucket.oss-cn-hangzhou.aliyuncs.com/key 的规范化 URI 是 /key;如果用 path 风格(官方示例如此)则是 /bucket/key,二者别混;
  7. 调试方法:--dry-run 把 CanonicalRequest 与 StringToSign 原文打印出来,与官方文档示例逐字符比对,99% 的 403 都能当场定位。

七、小结��下一篇预告

本篇把手写 OSS V4 签名完整落地:四步图解、130 行可运行实现、官方测试向量背书。核心心法一句话:签名 = 把"你到底想对谁做什么"规范化成一串字节,再用只有你和云知道的密钥对其 HMAC。

下一篇(第 2 篇)换 Java:手写 ACS3-HMAC-SHA256 签名调用 ECS DescribeInstances,体会"全系 OpenAPI 通用签名"与 OSS 专用签名的差异(Maven 完整工程同样会在仓库里)。欢迎收藏仓库:https://github.com/zhoubiao188/aliyun-v4-signature-examples

声明:本文为作者个人实践笔记,签名细节以阿里云官方签名文档为准;文中无任何真实密钥。

相关文章
|
2天前
|
弹性计算 安全 API
把 MCP server 放到阿里云上:出网、常驻与权限三件事
本文详解将MCP Server迁移至阿里云ECS的三大核心:**出网**(NAT+白名单)、**常驻**(systemd健康检查探协议层)、**权限**(降权/禁元数据/凭据隔离),直击远程化后“连得上但工具为空”的隐形故障,助你安全高效落地。
60 29
|
5天前
|
存储 弹性计算 供应链
阿里云服务器费用怎么算?CPU内存、公网带宽及存储收费标准(包年包月和按量付费)费用清单
阿里云ECS费用由计算(vCPU/内存)、存储(系统盘/数据盘)、公网带宽、镜像及快照等组成,支持包年包月(预付优惠)和按量付费(按秒计费)两种模式,适配长期业务或临时测试场景。实时价格因地域、规格而异,详见官网精准报价,阿里云云服务器ECS官网:https://t.aliyun.com/U/AZBUsA
|
9天前
|
固态存储 Java 测试技术
Elasticsearch 6.0.0本地单机16核32G压测报告
Elasticsearch 6.0.0本地单机16核32G压测报告
95 28
|
11天前
|
存储 安全 数据挖掘
在线教育系统源码与定制开发:企业搭建网校平台的技术方案解析
企业如何搭建属于自己的在线教育平台?本文从软件开发角度,解析在线教育系统源码与定制开发的区别,介绍课程管理、直播教学、学员管理、在线支付及学习数据分析等核心功能,并探讨视频播放、系统安全、性能扩展和项目实施流程,为教育培训机构、企业培训部门及知识付费平台规划网校系统提供参考。
|
12天前
|
弹性计算 负载均衡 安全
阿里云国际站服务代理商:SLB批量采购怎么做预算?按地域还是业务系统拆分
阿里云国际站SLB批量采购预算需兼顾地域(定价/部署)与业务系统(成本归属)双维度,避免重复计费;ALB/NLB/CLB计费模型不同,不可混用单价;应按实际资源清单(产品类型、网络类型、环境、周期)精细化报价与对账。
75 27
|
16天前
|
数据采集 监控 前端开发
数据质量问题怎么闭环?发现、定位、派单、整改、复核全流程拆解
本文深刻指出:企业数据质量困境不在“发现问题”,而在“闭环解决”。提出九大实践原则——从业务损失反推规则、分层定位根因、明确三类责任、优先源头整改、连续验证效果、聚焦处理效率、升级机制问题、正视管理本质、统一关单标准,强调质量治理本质是推动业务流程与系统规则的持续优化。(239字)
|
2月前
|
人工智能 开发者
阿里云优惠券是什么?在哪领取?如何使用?代金券使用指南
阿里云2026年最新优惠券指南:涵盖AI焕新季1728元券、学生300元无门槛券、企业上云补贴、域名口令及1元购活动。提供官方权益中心、高校计划、企业补贴三大领取入口,详解查询路径(费用→账户→卡券)与支付使用方法,助您省钱上云。
195 27
|
2月前
|
存储 弹性计算 人工智能
阿里云,最新云服务器ECS配置、价格,适用对象
这是一篇2026年阿里云ECS深度选型指南,涵盖实例族特性(g/c/r/t/gn系列)、价格策略、网络存储优化及实战避坑要点,助开发者避开隐藏成本,实现性能与成本的最优平衡。(239字)
155 27
|
2月前
|
数据采集 人工智能 JSON
面向大模型 RAG 的企业知识库工程化:Schema、信源与效果监测
本文提出面向大模型RAG的企业知识库工程化五层架构(GEO):L1 Schema结构化锚定实体,L2场景化FAQ提升召回,L3四级信源加权增强采信,L4全域信息一致性治理,L5可量化效果监测。聚焦“让大模型准确识别、优先采信、交叉验证”,提供可落地的技术方案与配置示例。(239字)
|
5月前
|
前端开发 JavaScript Serverless
阿里云宜搭对接使用完全指南:六大集成路径深度解析
本文系统性地阐述了阿里云宜搭低代码开发平台与外部系统对接的六大技术路径与实践方案。文章从宜搭的架构特性出发,深入讲解了连接器机制、远程API调用、数据源配置、服务回调、OpenAPI开放接口以及FaaS函数计算集成等核心对接方式。通过详细的配置步骤、丰富的代码示例(涵盖Java、Python、JavaScript、Node.js等语言)和真实业务场景的剖析,帮助开发者全面掌握宜搭的集成能力。文章还涵盖了鉴权配置、跨域处理、性能优化、安全管控等高级话题,并提供了常见问题的解决方案。无论是简单的数据查询同步,还是复杂的业务流程编排,读者都能从中找到适合的对接策略与实施方法。

热门文章

最新文章