PHP生成腾讯云COS接口需要的请求签名


Posted in PHP onMay 20, 2018

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:四个部分组成需要分开说
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=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个值都有了,有的是来自用户信息,有的需要计算,需要计算的上面也给出了所有的计算方法和为什么如此计算的个人理解。最后只需要按照官方要求进行输出即可。看一下?,在PostMan中选择Post方法,选择form-data方式提交数据,在Body中给出所有用户参数(这个地方为了测试算法是否与官方一直,所以几乎所有的值都是Post提交上去的,实际时间、Host都可以在算法中创建)

PHP生成腾讯云COS接口需要的请求签名

提交后,返回结果

PHP生成腾讯云COS接口需要的请求签名

字很小,单独把结果提取出来

{
 "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",
 "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]有什么关联。

PHP 相关文章推荐
用PHP的ob_start();控制您的浏览器cache!
Feb 14 PHP
中国站长站 For Dede4.0 采集规则
May 27 PHP
php下尝试使用GraphicsMagick的缩略图功能
Jan 01 PHP
PHP中=赋值操作符对不同数据类型的不同行为
Jan 02 PHP
JS中encodeURIComponent函数用php解码的代码
Mar 01 PHP
php中flush()、ob_flush()、ob_end_flush()的区别介绍
Feb 17 PHP
PHP swfupload图片上传的实例代码
Sep 30 PHP
基于JQuery+PHP编写砸金蛋中奖程序
Sep 08 PHP
PHP几个实用自定义函数小结
Jan 25 PHP
Zend Framework教程之分发器Zend_Controller_Dispatcher用法详解
Mar 07 PHP
PHP实现的mysql主从数据库状态检测功能示例
Jul 20 PHP
Yii框架 session 数据库存储操作方法示例
Nov 18 PHP
windows环境下使用Composer安装ThinkPHP5
May 18 #PHP
PHP实现登录验证码校验功能
May 17 #PHP
php识别翻转iphone拍摄的颠倒图片
May 17 #PHP
php通过各种函数判断0和空
Jul 04 #PHP
PHP手机短信验证码实现流程详解
May 17 #PHP
PHP实现多图上传和单图上传功能
May 17 #PHP
php实现姓名根据首字母排序的类与方法(实例代码)
May 16 #PHP
You might like
JAVA/JSP学习系列之六
2006/10/09 PHP
php中记录用户访问过的产品,在cookie记录产品id,id取得产品信息
2011/05/04 PHP
通过PHP修改Linux或Unix口令的方法分享
2012/01/30 PHP
PhpDocumentor 2安装以及生成API文档的方法
2014/05/21 PHP
PHP去掉json字符串中的反斜杠\及去掉双引号前的反斜杠
2015/09/30 PHP
Javascript document.referrer判断访客来源网址
2020/05/15 Javascript
js压缩工具 yuicompressor 使用教程
2010/03/31 Javascript
页面调用单个swf文件,嵌套出多个方法。
2011/11/21 Javascript
一行命令搞定node.js 版本升级
2014/07/20 Javascript
浅谈类似于(function(){}).call()的js语句
2015/03/30 Javascript
js实现拉幕效果的广告代码
2015/09/02 Javascript
AngularJS实现Model缓存的方式
2016/02/03 Javascript
在JavaScript中使用JSON数据
2016/02/15 Javascript
第三章之Bootstrap 表格与按钮功能
2016/04/25 Javascript
jQuery增加、删除及修改select option的方法
2016/08/19 Javascript
Javascript前端经典的面试题及答案
2017/03/14 Javascript
vue.js实现条件渲染的实例代码
2017/06/22 Javascript
详解使用Node.js 将txt文件转为Excel文件
2017/07/05 Javascript
判断div滑动到底部的scroll实例代码
2017/11/15 Javascript
JS简单判断是否在微信浏览器打开的方法示例
2019/01/08 Javascript
vue-router重定向和路由别名的使用讲解
2019/01/19 Javascript
ElementUI多个子组件表单的校验管理实现
2019/11/07 Javascript
[47:10]完美世界DOTA2联赛PWL S3 LBZS vs Rebirth 第二场 12.16
2020/12/18 DOTA
Python编程语言的35个与众不同之处(语言特征和使用技巧)
2014/07/07 Python
python实现linux下使用xcopy的方法
2015/06/28 Python
Python爬虫requests库多种用法实例
2020/05/28 Python
python递归函数用法详解
2020/10/26 Python
CSS3 优势以及网页设计师如何使用CSS3技术
2009/07/29 HTML / CSS
Lowe’s加拿大:家居装修、翻新和五金店
2019/12/06 全球购物
PHP解析URL是哪个函数?怎么用?
2013/05/09 面试题
给实习单位的感谢信
2014/02/01 职场文书
金融系应届毕业生求职信
2014/05/26 职场文书
党的群众路线批评与自我批评范文
2014/10/16 职场文书
党员个人总结范文
2015/02/14 职场文书
圆明园纪录片观后感
2015/06/03 职场文书
MySQL外键约束(FOREIGN KEY)案例讲解
2021/08/23 MySQL