最近项目有个删除上传到云点播的视频需求,由于本地php版本有点低(5.4+),SDK要求最低PHP5.6.33,所以只能根据API自己来;调用API最关键的部分恐怕就是这个【接口鉴权】的问题了,下面简单说一下本地实践中小田遇到的坑:
错误代码:AuthFailure.SignatureFailure (签名错误)
{"Response":{"Error":{"Code":"AuthFailure.SignatureFailure","Message":"The provided credentials could not be validated. Please check your signature is correct."},"RequestId":"1c56be9c-844f-43bf-ac2a-587be2b5aa7a"}}
于是小田我开始问度娘,然而并没有找到理想的解决方案,只能自己来了...
还好的是腾讯云提供了自己的工具 API Explorer 供用户在线调用、签名验证、生成 SDK 代码;
腾讯同时提供了各种语言生成密钥的demo,以下为PHP对应demo
<?php
$secretId = "AKIDz8krbsJ5yKBZQpn74WFkmLPx3EXAMPLE";
$secretKey = "Gu5t9xGARNpq86cd98joQYCN3EXAMPLE";
$host = "cvm.tencentcloudapi.com";
$service = "cvm";
$version = "2017-03-12";
$action = "DescribeInstances";
$region = "ap-guangzhou";
// $timestamp = time();
$timestamp = 1551113065;
$algorithm = "TC3-HMAC-SHA256";
// step 1: build canonical request string
$httpRequestMethod = "POST";
$canonicalUri = "/";
$canonicalQueryString = "";
$canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$host."\n";
$signedHeaders = "content-type;host";
$payload = '{"Limit": 1, "Filters": [{"Values": ["\u672a\u547d\u540d"], "Name": "instance-name"}]}';
$hashedRequestPayload = hash("SHA256", $payload);
$canonicalRequest = $httpRequestMethod."\n"
.$canonicalUri."\n"
.$canonicalQueryString."\n"
.$canonicalHeaders."\n"
.$signedHeaders."\n"
.$hashedRequestPayload;
echo $canonicalRequest.PHP_EOL;
// step 2: build string to sign
$date = gmdate("Y-m-d", $timestamp);
$credentialScope = $date."/".$service."/tc3_request";
$hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
$stringToSign = $algorithm."\n"
.$timestamp."\n"
.$credentialScope."\n"
.$hashedCanonicalRequest;
echo $stringToSign.PHP_EOL;
// step 3: sign string
$secretDate = hash_hmac("SHA256", $date, "TC3".$secretKey, true);
$secretService = hash_hmac("SHA256", $service, $secretDate, true);
$secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
$signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
echo $signature.PHP_EOL;
// step 4: build authorization
$authorization = $algorithm
." Credential=".$secretId."/".$credentialScope
.", SignedHeaders=content-type;host, Signature=".$signature;
echo $authorization.PHP_EOL;
$curl = "curl -X POST https://".$host
.' -H "Authorization: '.$authorization.'"'
.' -H "Content-Type: application/json; charset=utf-8"'
.' -H "Host: '.$host.'"'
.' -H "X-TC-Action: '.$action.'"'
.' -H "X-TC-Timestamp: '.$timestamp.'"'
.' -H "X-TC-Version: '.$version.'"'
.' -H "X-TC-Region: '.$region.'"'
." -d '".$payload."'";
echo $curl.PHP_EOL;
问题就来了,按照官方demo,最后通过php的cUrl提交时,出现签名错误的返回;
其中曲折这里我就不一一吐槽了,直接讲重点了,以下地方请格外注意:
1、demo中的
$payload = '{"Limit": 1, "Filters": [{"Values": ["\u672a\u547d\u540d"], "Name": "instance-name"}]}';
变量看着是个JSON格式,实际是个string,请使用json_encode(array)的形式转换自己的POST数据;比如小田我调用的是删除视频接口 需要传参如下 $payload = json_encode(['FileId'=>$FileId]);
2、demo中的step2里面的$canonicalHeaders与 API Explorer 中的对应部分不一样,API Explorer 中不包含charset=utf-8字符编码字段,导致第二步生成的$hashedRequestPayload 与 API Explorer 生成的不一致,demo中的可以去掉,注意:请将前面的“;”一同去掉;
3、圈重点:上面两步确定没问题的时候,签名部分基本结束;但是通过 Curl 提交依然出错;原因如下:
这是由于demo中step2中$canonicalHeaders变量我们去掉了【; charset=utf-8】部分造成的;重点!!!请求头中的这部分一定要与demo中step2步骤的$canonicalHeaders保持一致;如果上面去掉了【; charset=utf-8】,这里也需要去掉,如果上面不去掉这里也不要去掉,【; charset=utf-8】中“;”与“charset=utf-8”之间的空格 也要一样,上面有空格下面也需要有,不然就会提示 签名错误。。。血淋淋的教训,希望各位PHP攻城狮引以为戒。