鍵(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 參數(shù)列表為空 | |
q-signature | 14e6ebd7955b0c6da532151bf97045e2c5a64e10 | 通過代碼計算所得 |
但 q-signature 怎么來的?
剛才說到,q-signature 也需要特定算法計算得來,下面就說明如何計算
計算請求簽名
先看代碼:
/** * 計算簽名 * secretId、secretKey 為必需參數(shù),qSignStart、qSignEnd為調(diào)試需要,測試通過后應取消,改為方法內(nèi)自動創(chuàng)建 */ 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 中帶有 ?的請求參數(shù),該處應為數(shù)組排序后的字符串組合 $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); //組合結(jié)果 $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; }
為了測試,該方法參數(shù)應該是多過需要了,前六個參數(shù)是已經(jīng)給出的,是來自用戶的,因此直接賦值即可得到下邊字符串:
$authorization = "q-sign-algorithm=sha1q-ak=$secretIdq-sign-time=$qSignTimeq-key-time=$qKeyTime...
$header_list 這個值要符合 q-header-list
規(guī)則因此需要計算,邏輯是上文已經(jīng)描述,是從既定的請求項中抽出 key 組成有序字符串,代碼如下:
/** * 按COS要求對header_list內(nèi)容進行轉(zhuǎn)換 * 提取所有key * 字典排序 * key轉(zhuǎn)換為小寫 * 多對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請求參數(shù),對于 PUT 方法沒有 ? 參數(shù),自然值為空,所以代碼中“偷懶”直接給了空字符串。
Signature 的計算和需要小心的地方
官方已經(jīng)給出了完整的算法,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 說明在存儲桶根目錄下創(chuàng)建一個叫 testfile 的文件,/image/face1.jpg 說明在根目錄/image目錄下建立一個叫 face1.jpg 的文件,至于是不是圖片文件,不管
3、$httpParameters:這是第一個需要小心的地方。由HTTP原始請求參數(shù)組成,即請求 URI 中 ? 后面的部分,本例調(diào)用的是 PUT Object 接口,因此為空。如果不為空,需要把請求參數(shù)每一項的 key 和 value 均轉(zhuǎn)換小寫,多對 key=value 按字典排序并以 相連接
4、$headerString:這是第二個需要小心的地方,由 HTTP 原始請求頭組成,根據(jù)請求頭,選擇全部或部分請求頭,把每項的key都轉(zhuǎn)換為小寫,把value都進行URLEncode轉(zhuǎn)換,每項格式都改為key=value,然后按照key進行字典排序,最后把它們用連接符 組成字符串。這是我整理的邏輯,代碼如下:
/** * 按COS要求從數(shù)組中獲取 Signature 中 [HttpString] 內(nèi)容 * 標準格式 key=valuekey=value... * 數(shù)組元素按鍵字典排序 * * key轉(zhuǎn)換為小寫 * value進行UrlEncode轉(zhuǎn)換 * 轉(zhuǎn)換為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原始請求頭和請求參數(shù)用在了四個地方,分別是請求簽名里的 q-header-list 和 Signature 里的 HttpHeaders——兩者都用到了HTTP原始請求頭;請求簽名里的 q-url-param-list 和 Signature 里的 HttpParameters——兩者都用到了HTTP請求參數(shù)。一定要保證HTTP請求頭和請求參數(shù)所選用的數(shù)量和對象一致
輸出結(jié)果和校驗
至此,請求簽名中7個值都有了,有的是來自用戶信息,有的需要計算,需要計算的上面也給出了所有的計算方法和為什么如此計算的個人理解。最后只需要按照官方要求進行輸出即可??匆幌?#127792;,在PostMan中選擇Post方法,選擇form-data方式提交數(shù)據(jù),在Body中給出所有用戶參數(shù)(這個地方為了測試算法是否與官方一直,所以幾乎所有的值都是Post提交上去的,實際時間、Host都可以在算法中創(chuàng)建)
提交后,返回結(jié)果
字很小,單獨把結(jié)果提取出來
{ "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
部分,和官方文檔給出的結(jié)果值完全一致,說明算法邏輯正確。
吐槽和反思
version 0.2
昨天基于對騰訊云API的“憤慨”和怕忘記而急于記下思路的原因,寫的很是潦草,發(fā)覺吐槽人家官方文檔順序不同自己的更不同,今天重寫
version 0.1
之前 C# 做過一次對接口的研究,死活不行,最后通過騰訊技術支持提供的AWS的SDK調(diào)用成功,真是心累。本次需要用PHP做項目,必須要攻克,本來不應該多難,必須要為自己的智力和年齡討個說法。不過還是想再次吐槽官方文檔,看似詳盡,順序前后不夠一致,示例代碼細節(jié)比如參數(shù)不夠統(tǒng)一,造成新手容易誤解怎么前后對不上,對一些細節(jié)和前后邏輯不能第一時間融匯貫通。比如我自己,就是再次研究接口時,才理解里邊關于[SignHeaderList]
等和計算[Signature]
有什么關聯(lián)。