| 键(key) | 值(value) | 备注 |
|---|---|---|
| q-sign-algorithm | sha1 | 目前仅支持 sha1 签名算法 |
| q-ak | AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q | SecretId 字段 |
| q-sign-time | 1417773892;1417853898 | 2014/12/5 18:04:52 到 2014/12/6 16:18:18 |
| q-key-time | 1417773892;1417853898 | 2014/12/5 18:04:52 到 2014/12/6 16:18:18 |
| q-header-list | host;x-cos-content-sha1;x-cos-storage-class | HTTP 头部 key 的字典顺序排序列表 |
| q-url-param-list | HTTP 参数列表为空 | |
| q-signature | 14e6ebd7955b0c6da532151bf97045e2c5a64e10 | 通过代码计算所得 |
但 q-signature 怎么来的?
刚才说到,q-signature 也需要特定算法计算得来,下面就说明如何计算
计算请求签名
先看代码:
/**
* 计算签名
* secretId、secretKey 为必需参数,qSignStart、qSignEnd为调试需要,测试通过后应取消,改为方法内自动创建
*/
function get_authorization( $secretId, $secretKey, $qSignStart, $qSignEnd, $fileUri, $headers ){
/*
* 计算COS签名
* 2018-05-17
* author:cinlap cash216@163>
* ref:https://cloud.tencent.com/document/product/436/7778
*/
$qSignTime = "$qSignStart;$qSignEnd"; //unix_timestunix_timestamp
$qKeyTime = $qSignTime;
$header_list = get_q_header_list($headers);
//如果 Uri 中带有 ?的请求参数,该处应为数组排序后的字符串组合
$url_param_list = '';
//compute signature
$httpMethod = 'put';
$httpUri = $fileUri;
//与 q-url-param-list 相同
$httpParameters = $url_param_list;
//将自定义请求头分解为 连接的字符串
$headerString = get_http_header_string( $headers );
// 计算签名中的 signature 部分
$signTime = $qSignTime;
$signKey = hash_hmac('sha1', $signTime, $secretKey);
$httpString = "$httpMethod\n$httpUri\n$httpParameters\n$headerString\n";
$sha1edHttpString = sha1($httpString);
$stringToSign = "sha1\n$signTime\n$sha1edHttpString\n";
$signature = hash_hmac('sha1', $stringToSign, $signKey);
//组合结果
$authorization = "q-sign-algorithm=sha1q-ak=$secretIdq-sign-time=$qSignTimeq-key-time=$qKeyTimeq-header-list=$header_listq-url-param-list=$url_param_listq-signature=$signature";
return $authorization;
}
为了测试,该方法参数应该是多过需要了,前六个参数是已经给出的,是来自用户的,因此直接赋值即可得到下边字符串:
$authorization = "q-sign-algorithm=sha1q-ak=$secretIdq-sign-time=$qSignTimeq-key-time=$qKeyTime...
$header_list 这个值要符合 q-header-list 规则因此需要计算,逻辑是上文已经描述,是从既定的请求项中抽出 key 组成有序字符串,代码如下:
/**
* 按COS要求对header_list内容进行转换
* 提取所有key
* 字典排序
* key转换为小写
* 多对key=value之间用连接符连接
*
*/
function get_q_header_list($headers){
if(!is_array($headers)){
return false;
}
try{
$tmpArray = array();
foreach( $headers as $key=>$value){
array_push($tmpArray, strtolower($key));
}
sort($tmpArray);
return implode(';', $tmpArray);
}
catch(Exception $error){
return false;
}
}
$url-param-list 上面讲过,这个值是HTTP请求参数,对于 PUT 方法没有 ? 参数,自然值为空,所以代码中“偷懒”直接给了空字符串。
Signature 的计算和需要小心的地方
官方已经给出了完整的算法,PHP 甚至还有写好的代码,应该是很幸福了(但!由于看官方文档看的头晕还是踩了坑,随后一起说明),先看一下 signature 的“格式”:
SignKey = HMAC-SHA1(SecretKey,"[q-key-time]")
HttpString = [HttpMethod]\n[HttpURI]\n[HttpParameters]\n[HttpHeaders]\n
StringToSign = [q-sign-algorithm]\n[q-sign-time]\nSHA1-HASH(HttpString)\n
Signature = HMAC-SHA1(SignKey,StringToSign)
再看一下 Signature 的完整算法:
$signTime = $qSignTime;
$signKey = hash_hmac('sha1', $signTime, $secretKey);
$httpString = "$httpMethod\n$httpUri\n$httpParameters\n$headerString\n";
$sha1edHttpString = sha1($httpString);
$stringToSign = "sha1\n$signTime\n$sha1edHttpString\n";
$signature = hash_hmac('sha1', $stringToSign, $signKey);
$signTime:很简单,起止时间组成的字符串,从上文拿来直接用
$signKey:HMAC-SHA1 算法直接计算即可
$httpString:四个部分组成需要分开说
1、$httpMethod:HTTP请求方法,小写,比如 put、get
2、$httpUri:HTTP请求的URI部分,从“/”虚拟根开始,如 /testfile 说明在存储桶根目录下创建一个叫 testfile 的文件,/image/face1.jpg 说明在根目录/image目录下建立一个叫 face1.jpg 的文件,至于是不是图片文件,不管
3、$httpParameters:这是第一个需要小心的地方。由HTTP原始请求参数组成,即请求 URI 中 ? 后面的部分,本例调用的是 PUT Object 接口,因此为空。如果不为空,需要把请求参数每一项的 key 和 value 均转换小写,多对 key=value 按字典排序并以 相连接
4、$headerString:这是第二个需要小心的地方,由 HTTP 原始请求头组成,根据请求头,选择全部或部分请求头,把每项的key都转换为小写,把value都进行URLEncode转换,每项格式都改为key=value,然后按照key进行字典排序,最后把它们用连接符 组成字符串。这是我整理的逻辑,代码如下:
/**
* 按COS要求从数组中获取 Signature 中 [HttpString] 内容
* 标准格式 key=valuekey=value...
* 数组元素按键字典排序 *
* key转换为小写
* value进行UrlEncode转换
* 转换为key=value格式
* 多对key=value之间用连接符连接
*
*/
function get_http_header_string($headers){
if(!is_array($headers)){
return false;
}
try{
$tmpArray = array();
foreach($headers as $key => $value){
$tmpKey = strtolower($key);
$tmpArray[$tmpKey] = urlencode($value);
}
ksort($tmpArray);
$headerArray = array();
foreach( $tmpArray as $key => $value){
array_push($headerArray, "$key=$value");
}
return implode('', $headerArray);
}
catch(Exception $error){
return false;
}
}
为什么要小心?
HTTP原始请求头和请求参数用在了四个地方,分别是请求签名里的 q-header-list 和 Signature 里的 HttpHeaders——两者都用到了HTTP原始请求头;请求签名里的 q-url-param-list 和 Signature 里的 HttpParameters——两者都用到了HTTP请求参数。一定要保证HTTP请求头和请求参数所选用的数量和对象一致
输出结果和校验
至此,请求签名中7个值都有了,有的是来自用户信息,有的需要计算,需要计算的上面也给出了所有的计算方法和为什么如此计算的个人理解。最后只需要按照官方要求进行输出即可。看一下🌰,在PostMan中选择Post方法,选择form-data方式提交数据,在Body中给出所有用户参数(这个地方为了测试算法是否与官方一直,所以几乎所有的值都是Post提交上去的,实际时间、Host都可以在算法中创建)

提交后,返回结果

字很小,单独把结果提取出来
{
"Authorization": "q-sign-algorithm=sha1q-ak=AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5qq-sign-time=1417773892;1417853898q-key-time=1417773892;1417853898q-header-list=host;x-cos-content-sha1;x-cos-storage-classq-url-param-list=q-signature=14e6ebd7955b0c6da532151bf97045e2c5a64e10",
"Host": "bucket1-1254000000.cos.ap-beijing.myqcloud.com",
"Content-Length": "12000"
}
Host和Content-Length是我自定义输出,主要是看Authorization部分,和官方文档给出的结果值完全一致,说明算法逻辑正确。
吐槽和反思
version 0.2
昨天基于对腾讯云API的“愤慨”和怕忘记而急于记下思路的原因,写的很是潦草,发觉吐槽人家官方文档顺序不同自己的更不同,今天重写
version 0.1
之前 C# 做过一次对接口的研究,死活不行,最后通过腾讯技术支持提供的AWS的SDK调用成功,真是心累。本次需要用PHP做项目,必须要攻克,本来不应该多难,必须要为自己的智力和年龄讨个说法。不过还是想再次吐槽官方文档,看似详尽,顺序前后不够一致,示例代码细节比如参数不够统一,造成新手容易误解怎么前后对不上,对一些细节和前后逻辑不能第一时间融汇贯通。比如我自己,就是再次研究接口时,才理解里边关于[SignHeaderList]等和计算[Signature]有什么关联。