前言
腾讯云文档(自动交付接入):cloud.tencent.com/document/pr… 腾讯云文档(签名方法v3): cloud.tencent.com/document/ap…
起因
业务需求需要接入腾讯云授权,页面授权获取code还是很顺利的。我也很快拿到了code和signature并完成了合法性效验(自动交付接入:步骤4)。但是code效验接入的时候卡了一天半的时间。
解决
(重点):
这里的公共参数是要放到header头的他们的作用仅仅只是放在header头,根据图1的非必要结合图二就得到header必要参数。
(重点): 这里用的是v3签名方式 第一步 拼接规范请求串的CanonicalHeaders和SignedHeaders这两个参数上面,他们提供的说明是参与签名的头部信息,这个很容易误导成header头,其次他们的结果示例:
`content-type:application/json; charset=utf-8\nhost:cvm.tencentcloudapi.com\nx-tc-action:describeinstances\n`。
其中的x-tc-action也很难不让人误导以为这里需要跟header一样。 其实并不是 这里只需要必填的content-type和host即可。 我的数组:
[
'Content-Type'=>"application/json",
'Host'=>"open.tencentcloudapi.com",
];
#上代码 各位老板请吃
namespace app\controller\api\TencentCloud\actions;
class TencentCloudServer
{
//appid
public $app_id = "";
public $secret_id = "";
// protected $secret_id = "AKIDz8krbsJ5yKBZQpn74WFkmLPx3*******";
public $secret_key = "";
// protected $secret_key = "Gu5t9xGARNpq86cd98joQYCN3*******";
public $encry_key = "";
//回调url
public $redirect_url = "";
//授权url
public $url = "";
//请求url
public $sed_url = "";
//请求头
public $headers;
//第一步需要加签的头部
public $has_headers;
//时间戳
public $timestamp;
//时间戳日期
public $timestamp_data;
//URI 参数 (CanonicalURI)
public $uri;
//请求字符串 (CanonicalQueryString)
public $canonical_query_string;
//请求方式
public $method;
//参与签名的头部信息
public $canonical_header;
//参与签名的头部信息的头部
public $signed_headers;
//请求体也就是body
public $payload;
//接口名称
public $action;
//签名方法
public $algorithm ="TC3-HMAC-SHA256";
public function __construct($data=[])
{
$this->headers = $data['headers']??"";
$this->timestamp = time();
$this->timestamp_data = gmdate("Y-m-d", $this->timestamp);
$this->uri = $data['uri'] ?? "/";
$this->canonical_query_string = $data['canonical_query_string'] ??'';
$this->method = $data['method'] ??"POST";
$this->canonical_header = $data['canonical_header'] ??"";
$this->signed_headers = $data['signed_headers'] ??"";
$this->payload = $data['payload'] ??"";
$this->action = $data['action'] ??"";
}
public function checkMd5Signature($code, $signature)
{
$code_key = $code . $this->encry_key;
$sing = md5($code_key);
return $signature === $sing;
}
//拼接规范请求串
protected function getCanonicalRequest() {
$HTTPRequestMethod = strtoupper($this->method);
if ($HTTPRequestMethod === "POST") {
$this->canonical_query_string = "";
} else {
$this->canonical_query_string = http_build_query($this->canonical_query_string);
}
ksort($this->has_headers);
foreach ($this->has_headers as $key => $value) {
$this->canonical_header .= strtolower($key) . ":" . trim($value) . "\n";
$this->signed_headers .= strtolower($key) . ";";
}
$this->signed_headers = rtrim($this->signed_headers, ";");
$payloadHash = strtolower(hash('sha256', $this->payload));
$info = implode("\n", [
$HTTPRequestMethod,
$this->uri,
$this->canonical_query_string,
$this->canonical_header,
$this->signed_headers,
$payloadHash
]);
return $info;
}
//拼接待签名字符串
protected function getStringToSign($canonicalRequest) {
$hashCanonicalRequest = hash('sha256', $canonicalRequest);
$stringToSign = "TC3-HMAC-SHA256\n";
$stringToSign .= $this->timestamp . "\n";
$stringToSign .= $this->timestamp_data . "/" . $this->action . "/tc3_request\n";
$stringToSign .= $hashCanonicalRequest;
return $stringToSign;
}
protected function sign($key, $msg) {
return hash_hmac('sha256', $msg, $key, true);
}
//计算签名
protected function getSignature($stringToSign) {
$secretDate = $this->sign("TC3" . $this->secret_key, $this->timestamp_data);
$secretService = $this->sign($secretDate, $this->action);
$secretSigning = $this->sign($secretService, "tc3_request");
return hash_hmac('sha256', $stringToSign, $secretSigning);
}
//拼接 Authorization
protected function getAuthorization($signature) {
$credentialScope = $this->timestamp_data . "/" . $this->action . "/tc3_request";
$auth = $this->algorithm . " ";
$auth .= "Credential=" . $this->secret_id . "/" . $credentialScope . ", ";
$auth .= "SignedHeaders=".$this->signed_headers. ", ";
$auth .= "Signature=" . $signature;
return $auth;
}
public function tencentCloudV3Sign() {
$canonicalRequest = $this->getCanonicalRequest();
$stringToSign = $this->getStringToSign($canonicalRequest);
$signature = $this->getSignature($stringToSign);
return $this->getAuthorization($signature);
}
public function curl()
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->sed_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, array_map(function ($k, $v) { return "$k: $v"; }, array_keys($this->headers), $this->headers));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$response = json_decode($response,true);
return $response;
}
}
调用代码
class TencentCloudMarket extends AuthController
{
public function native()
{
Log::channel('tencent_cloud')->info('This is a log message to custom channel');
return "success";
}
public function authUrl()
{
$tencentCloud = new TencentCloudServer();
$query = http_build_query([
'scope' => 'login',
'app_id' => $tencentCloud->app_id,
'redirect_url' =>$tencentCloud->redirect_url,
// 'state' => md5(uniqid(rand(), true)),
]);
return bt_response($tencentCloud->url."?{$query}");
}
public function authLogin()
{
$params = $this->request->param([
'code', //授权code
'signature', // 签名
], null);
// 验证参数
validate([
'code' => 'require',
'signature' => 'require',
], [
'code.require' => '授权不能为空',
'signature.require' => '授权不能为空',
])->check($params);
$tencentCloud = new TencentCloudServer();
//code 的合法性做校验
$check = $tencentCloud->CheckMd5Signature($params['code'],$params['signature']);
if (!$check){
return bt_response('授权登陆失败','true','200');
}
//code 的合法性做校验
$time=time();
$tencentCloud->server = 'open';
$tencentCloud->uri = '/';
$tencentCloud->method = 'POST';
$tencentCloud->action = 'open';
$tencentCloud->timestamp = $time;
$tencentCloud->sed_url = "";
$tencentCloud->has_headers = [
'Content-Type'=>"application/json",
'Host'=>"open.tencentcloudapi.com",
];
$payload = json_encode([
'UserAuthCode'=>$params['code'],
]);
$tencentCloud->payload = $payload;
//获取token
$authorizationHeader = $tencentCloud->tencentCloudV3Sign();
//设置header头
$tencentCloud->headers = [
//注头部必须要空格不然签名直接异常
'X-TC-Action'=>"GetUserAccessToken",
'X-TC-Version'=>"2018-12-25",
'X-TC-Timestamp'=>$time,
'Authorization'=>$authorizationHeader,
];
//组合header头组成完整的header头部
$tencentCloud->headers = array_merge($tencentCloud->has_headers,$tencentCloud->headers);
$res = $tencentCloud->curl();
var_dump($res);die();
return bt_response();
}
收摊.文档千千万不可能维护到每一份,希望这一份对开发人员有帮助。相关问题反馈至腾讯云也是积极响应并做了跟进处理。十分感谢。