VolcEngineService.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace App\Services;
  3. use GuzzleHttp\Client;
  4. use GuzzleHttp\Exception\GuzzleException;
  5. class VolcEngineService
  6. {
  7. /** @var Client */
  8. protected $client;
  9. public function __construct()
  10. {
  11. $this->client = new Client([
  12. 'timeout' => 1200,
  13. ]);
  14. }
  15. /**
  16. * 通用请求方法:内部完成火山引擎签名并发送 HTTP 请求
  17. *
  18. * @param string $method HTTP 方法:GET / POST / PUT ...
  19. * @param string $host 目标 Host,例如:iam.volcengineapi.com
  20. * @param string $path 请求路径,例如:"/" 或 "/"
  21. * @param array $query 查询参数(不含 Action/Version)
  22. * @param string $body 请求 Body 字符串(GET 一般为空字符串)
  23. * @param string $ak AccessKeyId
  24. * @param string $sk SecretAccessKey
  25. * @param string $service 服务名,例如:iam / vod / sms 等
  26. * @param string $region 区域,例如:cn-north-1
  27. * @param string $action 公共参数 Action
  28. * @param string $version 公共参数 Version
  29. * @param string $contentType Content-Type,默认 application/json
  30. *
  31. * @return array [
  32. * 'status' => int,
  33. * 'headers' => array,
  34. * 'body' => string,
  35. * ]
  36. * @throws GuzzleException
  37. */
  38. public function request(
  39. string $method,
  40. string $host,
  41. string $path,
  42. array $query,
  43. string $body,
  44. string $ak,
  45. string $sk,
  46. string $service,
  47. string $region,
  48. string $action,
  49. string $version,
  50. string $contentType = 'application/json'
  51. ): array {
  52. // 初始化身份证明结构体
  53. $credential = [
  54. 'accessKeyId' => $ak,
  55. 'secretKeyId' => $sk,
  56. 'service' => $service,
  57. 'region' => $region,
  58. ];
  59. // 合并公共参数,并按 key 排序
  60. $query = array_merge($query, [
  61. 'Action' => $action,
  62. 'Version' => $version
  63. ]);
  64. ksort($query);
  65. // 初始化请求参数
  66. $requestParam = [
  67. 'body' => $body,
  68. 'host' => $host,
  69. 'path' => $path,
  70. 'method' => $method,
  71. 'contentType' => $contentType,
  72. 'date' => gmdate('Ymd\\THis\\Z'),
  73. 'query' => $query
  74. ];
  75. // 计算签名
  76. $xDate = $requestParam['date'];
  77. $shortXDate = substr($xDate, 0, 8);
  78. $xContentSha256 = hash('sha256', $requestParam['body']);
  79. $signResult = [
  80. 'Host' => $requestParam['host'],
  81. 'X-Content-Sha256' => $xContentSha256,
  82. 'X-Date' => $xDate,
  83. 'Content-Type' => $requestParam['contentType']
  84. ];
  85. // 计算 Signature 签名
  86. $signedHeaderStr = join(';', ['content-type', 'host', 'x-content-sha256', 'x-date']);
  87. $canonicalRequestStr = join("\n", [
  88. $requestParam['method'],
  89. $requestParam['path'],
  90. http_build_query($requestParam['query']),
  91. join("\n", [
  92. 'content-type:'. $requestParam['contentType'],
  93. 'host:'. $requestParam['host'],
  94. 'x-content-sha256:'. $xContentSha256,
  95. 'x-date:'. $xDate
  96. ]),
  97. '',
  98. $signedHeaderStr,
  99. $xContentSha256
  100. ]);
  101. $hashedCanonicalRequest = hash("sha256", $canonicalRequestStr);
  102. $credentialScope = join('/', [$shortXDate, $credential['region'], $credential['service'], 'request']);
  103. $stringToSign = join("\n", [
  104. 'HMAC-SHA256',
  105. $xDate,
  106. $credentialScope,
  107. $hashedCanonicalRequest
  108. ]);
  109. $kDate = hash_hmac("sha256", $shortXDate, $credential['secretKeyId'], true);
  110. $kRegion = hash_hmac("sha256", $credential['region'], $kDate, true);
  111. $kService = hash_hmac("sha256", $credential['service'], $kRegion, true);
  112. $kSigning = hash_hmac("sha256", 'request', $kService, true);
  113. $signature = hash_hmac("sha256", $stringToSign, $kSigning);
  114. $signResult['Authorization'] = sprintf(
  115. "HMAC-SHA256 Credential=%s, SignedHeaders=%s, Signature=%s",
  116. $credential['accessKeyId']. '/'. $credentialScope,
  117. $signedHeaderStr,
  118. $signature
  119. );
  120. $header = array_merge([], $signResult); // 空的$header数组
  121. // 发送 HTTP 请求
  122. $client = new Client([
  123. 'base_uri' => 'https://'. $requestParam['host'],
  124. 'timeout' => 1200,
  125. ]);
  126. $response = $client->request($method, $requestParam['path'], [
  127. 'headers' => $header,
  128. 'query' => $requestParam['query'],
  129. 'body' => $requestParam['body']
  130. ]);
  131. $responseContent = $response->getBody()->getContents();
  132. // 转换 \u0026 为 &
  133. $responseContent = str_replace('\u0026', '&', $responseContent);
  134. return [
  135. 'status' => $response->getStatusCode(),
  136. 'headers' => $response->getHeaders(),
  137. 'body' => $responseContent,
  138. ];
  139. }
  140. }