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压缩多个CSS为一个css的代码并缓存
Apr 21 PHP
Linux fgetcsv取得的数组元素为空字符串的解决方法
Nov 25 PHP
PHP的SQL注入过程分析
Jan 06 PHP
php判断是否为json格式的方法
Mar 04 PHP
php生成二维码时出现中文乱码的解决方法
Dec 18 PHP
php similar_text()函数的定义和用法
May 12 PHP
PHP加密解密类实例代码
Jul 20 PHP
php封装的mysqli类完整实例
Oct 18 PHP
php使用gd2绘制基本图形示例(直线、圆、正方形)
Feb 15 PHP
php使用ftp实现文件上传与下载功能
Jul 21 PHP
Laravel框架中Blade模板的用法示例
Aug 30 PHP
PHP扩展mcrypt实现的AES加密功能示例
Jan 29 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
PHP提示Cannot modify header information - headers already sent by解决方法
2014/09/22 PHP
php数组添加元素方法小结
2014/12/20 PHP
JS日期和时间选择控件升级版(自写)
2013/08/02 Javascript
jquery任意位置浮动固定层插件用法实例
2015/05/29 Javascript
javascript实现支持移动设备画廊
2015/08/24 Javascript
Bootstrap入门书籍之(零)Bootstrap简介
2016/02/17 Javascript
jQuery实现图片轮播效果代码(基于jquery.pack.js插件)
2016/06/02 Javascript
bootstrap学习笔记之初识bootstrap
2016/06/21 Javascript
AngularJs html compiler详解及示例代码
2016/09/01 Javascript
AngularJS  双向数据绑定详解简单实例
2016/10/20 Javascript
利用JS轻松实现获取表单数据
2016/12/06 Javascript
vue.js选中动态绑定的radio的指定项
2017/06/02 Javascript
Vue实现本地购物车功能
2018/12/05 Javascript
js实现弹出框的拖拽效果实例代码详解
2019/04/16 Javascript
简单了解JS打开url的方法
2020/02/21 Javascript
javascript实现简单搜索功能
2020/03/26 Javascript
原生JS封装拖动验证滑块的实现代码示例
2020/06/01 Javascript
Express 配置HTML页面访问的实现
2020/11/01 Javascript
编写Python小程序来统计测试脚本的关键字
2016/03/12 Python
Python Socket实现简单TCP Server/client功能示例
2017/08/05 Python
Python基于Floyd算法求解最短路径距离问题实例详解
2018/05/16 Python
解决python中os.listdir()函数读取文件夹下文件的乱序和排序问题
2018/10/17 Python
Python描述符descriptor使用原理解析
2020/03/21 Python
python爬取youtube视频的示例代码
2021/03/03 Python
Pretty Little Thing美国:时尚女性服饰
2018/08/27 全球购物
KENZO官网:高田贤三在法国创立的品牌
2019/05/16 全球购物
端午节粽子促销活动方案
2014/02/02 职场文书
社会实践先进工作者事迹材料
2014/05/06 职场文书
优秀党务工作者事迹材料
2014/05/07 职场文书
银行青年文明号事迹材料
2014/05/31 职场文书
刑事代理授权委托书
2014/09/17 职场文书
部队2015年终工作总结
2015/04/02 职场文书
2015年手术室工作总结
2015/05/11 职场文书
毕业典礼致辞
2015/07/29 职场文书
python 通过使用Yolact训练数据集
2021/04/06 Python
动视暴雪取消疫苗禁令 让所有员工返回线下工作
2022/04/03 其他游戏