单文件PHP实现腾讯云APIv3签名及访问

腾讯云SDK需要导入的代码文件过多,影响项目的可读性和可维护性,于是,决定自己封装一个简单的SDK。此基础类签名算法符合腾讯云APIv3规范,可大大简化脱离官方SDK进行单一产品接口开发的难度。相关的文章推荐:纯shell实现腾讯云APIv3签名及访问

/**
 * 腾讯云基础类
 * @author rehiy
 * @url https://www.rehiy.com/post/527/
 */
class QCloudBasic
{
    /**
     * @var string 接口域名
     */
    protected $domain = 'tencentcloudapi.com';

    /**
     * @var string 接口服务
     */
    protected $service = '';

    /**
     * @var string 接口版本
     */
    protected $version = '';

    /**
     * @var string SecretId
     */
    protected $secretId = '';

    /**
     * @var string SecretKey
     */
    protected $secretKey = '';

    /**
     * 构造函数
     * @param array $config
     */
    public function __construct($config)
    {
        $this->domain = $this->service . '.' . $this->domain;
        foreach ($config as $key => $value) {
            $this->$key = $value;
        }
    }

    /**
     * 请求接口
     * @param string $region 地域
     * @param string $action 操作名称
     * @param array $data 请求参数
     */
    protected function post($region, $action, $data)
    {
        $timestamp = time();
        $payload = empty($data) ? '{}' : json_encode($data, 320);
        $authorization = $this->signature($action, $payload, $timestamp);

        $headers = [
            "Authorization: {$authorization}",
            "Content-Type: application/json; charset=utf-8",
            "Host: {$this->domain}",
            "X-TC-Action: {$action}",
            "X-TC-Timestamp: {$timestamp}",
            "X-TC-Version: {$this->version}",
            "X-TC-Region: {$region}"
        ];

        return $this->httpRequest('POST', "https://{$this->domain}", $payload, $headers);
    }

    /**
     * 生成签名
     * @param string $action 操作名称
     * @param string $payload 请求参数
     * @param number $timestamp 时间戳
     */
    protected function signature($action, $payload, $timestamp)
    {
        $algorithm = 'TC3-HMAC-SHA256';
        $date = gmdate('Y-m-d', $timestamp ?: time());

        $canonicalUri = '/';
        $canonicalQuery = '';
        $canonicalHeaders = implode("\n", [
            'content-type:application/json; charset=utf-8',
            'host:' . $this->domain,
            'x-tc-action:' . strtolower($action),
            ''
        ]);

        $signedHeaders = 'content-type;host;x-tc-action';
        $canonicalRequest = implode("\n", [
            'POST',
            $canonicalUri,
            $canonicalQuery,
            $canonicalHeaders,
            $signedHeaders,
            hash('SHA256', $payload)
        ]);

        $credentialScope = "{$date}/{$this->service}/tc3_request";
        $stringToSign = implode("\n", [
            $algorithm,
            $timestamp,
            $credentialScope,
            hash('SHA256', $canonicalRequest),
        ]);

        $secretDate = hash_hmac('SHA256', $date, 'TC3' . $this->secretKey, true);
        $secretService = hash_hmac('SHA256', $this->service, $secretDate, true);
        $secretSigning = hash_hmac('SHA256', 'tc3_request', $secretService, true);
        $signature = hash_hmac('SHA256', $stringToSign, $secretSigning);

        return implode(', ', [
            "{$algorithm} Credential={$this->secretId}/{$credentialScope}",
            "SignedHeaders={$signedHeaders}",
            "Signature={$signature}"
        ]);
    }

    /**
     * 发送HTTP请求
     * @param string $method 请求方式
     * @param string $url 服务器地址
     * @param array|string $data 数据
     * @param array $header 请求头
     */
    protected function httpRequest($method, $url, $data = null, $header = null)
    {
        $ch = curl_init($url);

        curl_setopt($ch, CURLOPT_TIMEOUT, 25);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header ?? []);

        if ($data && $method == 'POST') {
            if (is_array($data)) {
                if (in_array('Content-Type: application/json', $header)) {
                    $data = json_encode($data, 320);
                } else {
                    $data = http_build_query($data);
                }
            }
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }

        list($body, $errno, $error) = [
            curl_exec($ch), curl_errno($ch), curl_error($ch), curl_close($ch)
        ];

        if ($errno != 0) {
            throw new \Exception('CURL - ' . $error, $errno);
        }

        try {
            $res = json_decode($body, true);
            return $res['Response'] ?? '';
        } catch (\Exception $e) {
            return false;
        }
    }
}
文章作者: 若海; 原文链接: https://www.rehiy.com/post/545/; 转载需声明来自技术写真 - 若海

添加新评论