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获取地址栏信息的代码
Oct 08 PHP
字母顺序颠倒而单词顺序不变的php代码
Aug 08 PHP
PHP Imagick完美实现图片裁切、生成缩略图、添加水印
Feb 22 PHP
Yii框架中sphinx索引配置方法解析
Oct 18 PHP
Zend Framework使用Zend_Loader组件动态加载文件和类用法详解
Dec 09 PHP
PHP+Ajax 检测网络是否正常实例详解
Dec 16 PHP
Yii2框架数据验证操作实例详解
May 02 PHP
php如何利用pecl安装mongodb扩展详解
Jan 09 PHP
PHP模糊查询技术实例分析【附源码下载】
Mar 07 PHP
PHP pthreads v3在centos7平台下的安装与配置操作方法
Feb 21 PHP
php实现记事本案例
Oct 20 PHP
Laravel的加密解密与哈希实例讲解
Mar 24 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
从零开始学YII2框架(二)通过 Composer 安装扩展插件
2014/08/20 PHP
javascript:void(0)的真正含义实例分析
2008/08/20 Javascript
js 无提示关闭浏览器页面的代码
2010/03/09 Javascript
ASP.NET jQuery 实例10 动态修改hyperlink的URL值
2012/02/03 Javascript
JavaScript省市联动实现代码
2014/02/15 Javascript
javascript中substring()、substr()、slice()的区别
2015/08/30 Javascript
jQuery事件详解
2017/02/23 Javascript
完美实现js选项卡切换效果(二)
2017/03/08 Javascript
ReactNative短信验证码倒计时控件的实现代码
2017/07/20 Javascript
详解基于webpack2.x的vue2.x的多页面站点
2017/08/21 Javascript
JavaScript中的E-mail 地址格式验证
2018/03/28 Javascript
jQuery实现输入框的放大和缩小功能示例
2018/07/21 jQuery
初学node.js中实现删除用户路由
2019/05/27 Javascript
jsonp格式前端发送和后台接受写法的代码详解
2019/11/07 Javascript
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
2020/12/25 Vue.js
21行Python代码实现拼写检查器
2016/01/25 Python
Python中MySQL数据迁移到MongoDB脚本的方法
2016/04/28 Python
Python简单计算给定某一年的某一天是星期几示例
2018/06/27 Python
python检测文件夹变化,并拷贝有更新的文件到对应目录的方法
2018/10/17 Python
python使用PIL实现多张图片垂直合并
2019/01/15 Python
使用Python和Scribus创建一个RGB立方体的方法
2019/07/17 Python
Django在pycharm下修改默认启动端口的方法
2019/07/26 Python
Python 异步协程函数原理及实例详解
2019/11/13 Python
使用python实现画AR模型时序图
2019/11/20 Python
NumPy排序的实现
2020/01/21 Python
Scrapy框架基本命令与settings.py设置
2020/02/06 Python
详解numpy1.19.4与python3.9版本冲突解决
2020/12/15 Python
中国跨境电子商务网站:NewFrog
2018/03/10 全球购物
网络工程与软件技术毕业生自荐信
2013/09/24 职场文书
连锁酒店店长职责范本
2014/02/13 职场文书
实习介绍信模板
2015/01/30 职场文书
工作调动申请报告
2015/05/18 职场文书
企业宣传稿范文
2015/07/23 职场文书
JDBC连接的六步实例代码(与mysql连接)
2021/05/12 MySQL
Java工作中实用的代码优化技巧分享
2022/04/21 Java/Android
Python测试框架pytest高阶用法全面详解
2022/06/01 Python