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图片验证码代码
Mar 27 PHP
深入PHP中慎用双等于(==)的详解
Jun 06 PHP
PHP图片上传代码
Nov 04 PHP
php $_SERVER windows系统与linux系统下的区别说明
Feb 14 PHP
php中spl_autoload详解
Oct 17 PHP
ThinkPHP做文字水印时提示call an undefined function exif_imagetype()解决方法
Oct 30 PHP
PHP采用curl模仿用户登陆新浪微博发微博的方法
Nov 07 PHP
php读取文件内容到数组的方法
Mar 16 PHP
PHP中类的自动加载的方法
Mar 17 PHP
PHP实现在对象之外访问其私有属性private及保护属性protected的方法
Nov 20 PHP
Yii框架分页技术实例分析
Aug 30 PHP
PHP判断当前使用的是什么浏览器(推荐)
Oct 27 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把数字转成人民币大写的函数分享
2014/06/30 PHP
自定义session存储机制避免会话保持问题
2014/10/08 PHP
使用PHPCMS搭建wap手机网站
2015/09/20 PHP
Zend Framework框架之Zend_Mail实现发送Email邮件验证功能及解决标题乱码的方法
2016/03/21 PHP
详细解读php的命名空间(一)
2018/02/21 PHP
javascript实现颜色渐变的方法
2013/10/30 Javascript
javascript列表框操作函数集合汇总
2013/11/28 Javascript
JQuery实现动态添加删除评论的方法
2015/05/18 Javascript
javascript图片预加载实例分析
2015/07/16 Javascript
编写高质量JavaScript代码的基本要点
2016/03/02 Javascript
EasyUI的doCellTip实现鼠标放到单元格上提示单元格内容
2016/08/24 Javascript
微信小程序 数据封装,参数传值等经验分享
2017/01/09 Javascript
vue实现简单表格组件实例详解
2017/04/16 Javascript
jQuery Layer弹出层传值到父页面的实现代码
2017/08/17 jQuery
JS关于刷新页面的相关总结
2018/05/09 Javascript
JS函数基本定义与用法示例
2020/01/15 Javascript
Python使用PIL库实现验证码图片的方法
2016/03/11 Python
Python打包文件夹的方法小结(zip,tar,tar.gz等)
2016/09/18 Python
Python使用matplotlib简单绘图示例
2018/02/01 Python
使用pandas的DataFrame的plot方法绘制图像的实例
2018/05/24 Python
django实现用户注册实例讲解
2019/10/30 Python
python 中的[:-1]和[::-1]的具体使用
2020/02/13 Python
CSS3中的transform属性进行2D和3D变换的基本用法
2016/05/12 HTML / CSS
中国高端家电购物商城:顺电
2018/03/04 全球购物
美国保健品专家:Life Extension
2018/05/04 全球购物
青年文明号复核材料
2014/02/11 职场文书
教学改革实施方案
2014/03/31 职场文书
实习生岗位职责
2014/04/12 职场文书
俄语专业毕业生求职信
2014/07/12 职场文书
培训科主任岗位职责
2014/08/08 职场文书
排球赛新闻稿
2015/07/17 职场文书
SqlServer 垂直分表(减少程序改动)
2021/04/16 SQL Server
Django drf请求模块源码解析
2021/06/08 Python
Nginx图片服务器配置之后图片访问404的问题解决
2022/03/21 Servers
win11开机发生死循环重启怎么办?win11开机发生死循环重启解决方法
2022/08/05 数码科技
插件导致ECharts被全量引入的坑示例解析
2022/09/23 Javascript