目標 使用 PHP 創建 COS 介面所需要的請求簽名,按照官方示例,請求簽名應用在需要身份校驗的場景,即非公有讀許可權時。否則在請求API介面時,就必須攜帶簽名作為請求頭的一部分傳遞。 步驟 準備好用戶信息 1. 將會使用到的用戶信息包括: SecretId:騰訊雲賬號內分配 SecretKey:騰 ...
COS和請求簽名是什麼
COS 是騰訊雲對象存儲的縮寫及簡稱,請求簽名是第三方在調用COS相關介面時需要按需提供的、經過特定演算法創建而成的一組字元串信息,將唯一的標識當前第三方身份,提供通信雙方的身份識別,只有有效的簽名COS才會提供服務
目標
使用 PHP 創建 COS 介面所需要的請求簽名,與官方文檔給出的示例做比較,驗證演算法的正確性
認識請求簽名
先來看一條官方文檔給出的請求簽名的樣子
q-sign-algorithm=sha1&q-ak=[SecretID]&q-sign-time=[SignTime]&q-key-time=[KeyTime]&q-header-list=[SignedHeaderList]&q-url-param-list=[SignedParameterList]&q-signature=[Signature]
請求簽名特點總結
- 是一串字元串
- key=value的鍵值對格式,key為固定值
- 一共有7對key=value
- sha1也是參數,但截止到官方發文只支持sha1,因此可以直接賦值
- SignedHeaderList、SignedParameterList、Signature三個value需要通過演算法生成
鍵值對的具體描述參見官方文檔。
逐個擊破
請求簽名一共需要7個值,下麵一一講解,各個擊破
q-sign-algorithm
簽名演算法,官方目前僅支持 sha1,因此直接給值即可
q-ak
賬戶ID,即用戶的 SecretId,可以在控制台 雲API密鑰 頁面獲取
q-sign-time
當前簽名的有效起止時間,Unix時間戳格式,英文半形分號 ;
分割,格式如 1480932292;1481012298
q-key-time
與 q-sign-time 值相同
q-header-list
個人理解,由HTTP請求頭組成,取全部或部分請求頭,將 key:value
形式的請求項的 key 部分取出,轉化小寫,多個 key 按字典排序,以字元 ;
連接,最終組成字元串
如原始請求頭有兩個:
Host:bucket1-1254000000.cos.ap-beijing.myqcloud.com Content-Type:image/jpeg
key 就是 Host 和 Content-Type,經過運算後輸出 content-type;host
q-url-param-list
個人理解,由HTTP請求參數組成,取全部或部分請求參數,將 key=value
形式的請求參數的 key 部分取出,轉化小寫,多個 key 按字典排序,以字元 ;
連接,最終組成字元串
如原始HTTP請求為:
GET /?prefix=abc&max-keys=20
key 就是 prefix 和 max-keys,經過運算後輸出 max-keys;prefix
,如果請求沒有參數比如 put、post,此處即為空
q-signature
根據HTTP內容計算簽名,演算法由COS提供,只需按要求給值
官方示例及參照結果
在開始編寫邏輯之前,先看一下官方示例給出的參考值,以及經過計算後的結果,以便和自己開發的邏輯進行結果比對
HTTP原始請求,也可以理解為計算簽名前或不需要簽名時的HTTP請求:
PUT /testfile2 HTTP/1.1 Host: bucket1-1254000000.cos.ap-beijing.myqcloud.com x-cos-content-sha1: 7b502c3a1f48c8609ae212cdfb639dee39673f5e x-cos-storage-class: standard Hello world
計算簽名後應該得到的HTTP請求:
PUT /testfile2 HTTP/1.1 Host: bucket1-1254000000.cos.ap-beijing.myqcloud.com x-cos-content-sha1: 7b502c3a1f48c8609ae212cdfb639dee39673f5e x-cos-storage-class: standard Authorization: q-sign-algorithm=sha1&q-ak=AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q&> q-sign-time=1417773892;1417853898&q-key-time=1417773892;1417853898&q-header-list=host;x-cos-content-sha1;x-cos-storage-class&q-url-param-list=&q-signature=14e6ebd7955b0c6da532151bf97045e2c5a64e10 Hello world
結論:演算法如果能得到 Authorization 後的那一串字元串即為正確
準備工作
來看一下(官方提供的)用戶信息以及HTTP信息:
- SecretId:AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q
- SecretKey:BQYIM75p8x0iWVFSIgqEKwFprpRSVHlz
- 簽名有效起始時間:1417773892
- 簽名有效停止時間:1417853898
- HTTP原始請求頭:根據上一節示例不難得到HTTP原始請求有三項內容 Host、x-cos-content-sha1 和 x-cos-storage-class
- HTTP請求參數:是 PUT 請求,沒有 ? 參數
計算簽名
將準備工作中的各項參數帶入請求簽名規則,不難就可以得到結果,如下表:
鍵(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_timestamp;unix_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=sha1&q-ak=$secretId&q-sign-time=$qSignTime&q-key-time=$qKeyTime&q-header-list=$header_list&q-url-param-list=$url_param_list&q-signature=$signature";
return $authorization;
}
為了測試,該方法參數應該是多過需要了,前六個參數是已經給出的,是來自用戶的,因此直接賦值即可得到下邊字元串:
$authorization = "q-sign-algorithm=sha1&q-ak=$secretId&q-sign-time=$qSignTime&q-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:四個部分組成需要分開說
- $httpMethod:HTTP請求方法,小寫,比如 put、get
- $httpUri:HTTP請求的URI部分,從“/”虛擬根開始,如
/testfile
說明在存儲桶根目錄下創建一個叫testfile
的文件,/image/face1.jpg
說明在根目錄/image目錄下建立一個叫face1.jpg
的文件,至於是不是圖片文件,不管 - $httpParameters:這是第一個需要小心的地方。由HTTP原始請求參數組成,即請求 URI 中 ? 後面的部分,本例調用的是 PUT Object 介面,因此為空。如果不為空,需要把請求參數每一項的 key 和 value 均轉換小寫,多對 key=value 按字典排序並以 & 相連接
- $headerString:這是第二個需要小心的地方,由 HTTP 原始請求頭組成,根據請求頭,選擇全部或部分請求頭,把每項的key都轉換為小寫,把value都進行URLEncode轉換,每項格式都改為key=value,然後按照key進行字典排序,最後把它們用連接符 & 組成字元串。這是我整理的邏輯,代碼如下:
/**
* 按COS要求從數組中獲取 Signature 中 [HttpString] 內容
* 標準格式 key=value&key=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請求頭和請求參數所選用的數量和對象一致
- 相同:生成 q-header-list 的HTTP請求頭數量和成員要和生成 HttpHeaders 的相同,生成 q-url-param-list 的HTTP請求參數數量和成員要和生成 HttpParameters 的相同
- 不同:q-header-list 和 q-url-param-list 只取 key 部分,HttpHeaders 和 HttpParameters 取 key 和 value 部分
輸出結果和校驗
至此,請求簽名中7個值都有了,有的是來自用戶信息,有的需要計算,需要計算的上面也給出了所有的計算方法和為什麼如此計算的個人理解。最後只需要按照官方要求進行輸出即可。看一下