写在前面:为什么你总是遇到 403 Forbidden?
如果你正在开发一个需要上传图片或文件到青云对象存储(QingStor)的项目,大概率会卡在“权限校验”这一关。明明 Access Key 和 Secret Key 都对,URL 也复制对了,但请求就是报 403 Forbidden,甚至有时候是 SignatureDoesNotMatch。
别慌,这通常不是密钥错了,而是签名算法没搞对。
对象存储(OSS)为了安全,绝大多数公开接口都要求对请求进行签名验证。这就好比你寄快递不仅要写好收件地址,还得贴上快递单并签字确认,否则快递员(服务器)有权拒收。青云对象存储采用的是 HTTP Signature 方案,核心思想是:把请求的关键信息(方法、路径、头信息、时间等)通过 HMAC-SHA256 算法算出一个指纹,附带在请求里发给服务器,服务器用同样的方式算一遍,对比是否一致。
今天我们就把这个“指纹”怎么生成的原理彻底讲清楚,并给出 Python、Java、Node.js、C++ 四个主流语言的可运行代码,最后附上排查清单,保证你看完不再头秃。
一、签名核心原理拆解
在动手写代码之前,必须理解签名的构成。青云对象存储的签名遵循 RFC 2617 风格的自定义实现,主要依赖以下几个要素:
1. 关键概念
- Method:HTTP 方法,如
GET、PUT、DELETE。 - Resource:资源路径,格式为
/bucket-name/key-name。注意:bucket 和 key 之间用/分隔,没有开头的/(但在某些内部计算中会补回)。 - Headers:需要参与签名的请求头,主要是
Content-MD5、Content-Type和Date。 - Date:请求的时间戳,格式必须严格遵循 RFC 2616 的
wkday, dd mon yyyy hh:mm:ss GMT,例如Tue, 20 Oct 2023 08:08:08 GMT。这是最容易出错的地方,时区必须是 UTC+0。 - Authorization:最终的授权字符串。
2. 字符串签名(StringToSign)
签名的本质是对一个字符串计算 HMAC-SHA256。这个字符串由以下部分组成,按顺序用 \n(换行符)连接:
METHOD
Content-MD5
Content-Type
Date
Headers
/AccessKeyID/Resource
具体解释:
- 第一行:HTTP 方法,小写?不,通常是大写,如
GET。 - 第二行:
Content-MD5,如果请求没有设置该头,则这一行留空(即只有一个换行符)。如果是上传文件,通常需要计算文件的 MD5。 - 第三行:
Content-Type,如application/json,没有则留空。 - 第四行:
Date,即请求的 GMT 时间,如果没有Date头,通常由客户端生成。 - 第五行:额外的请求头,格式为
header-name:header-value\n,按字母顺序排序。如果没有则留空。 - 第六行:
/AccessKeyID/Resource。AccessKeyID 是你的 ID,Resource 是路径,前面带一个/。
3. 最终签名
Signature = Base64(HMAC-SHA256(StringToSign, SecretKey))
4. Authorization 头格式
Authorization: QS accessKeyId:Signature
例如: “` Authorization: QS QN3L5T2W6X8Y:U3x9K1mP4nQ7rS2vY5zA8bC1dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN8pQ0rS2tU4vW6xY8zA0bC2dE4fG6hJ8kL0mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hJ2kL4mN6pQ8rS0tU2vW4xY6zA8bC0dE2fG4hJ6kL8mN0pQ2rS4tU6vW8xY0zA2bC4dE6fG8hJ0kL2mN4pQ6rS8tU0vW2xY4zA6bC8dE0fG2hJ4kL6mN
