本文目录导读:

在 PHP 中实现 AWS Signature V4 签名,主要有两种方式:
使用 AWS SDK(推荐)
最简单可靠的方式是使用 AWS SDK for PHP:
<?php
require 'vendor/autoload.php';
use Aws\Signature\SignatureV4;
use Aws\Credentials\Credentials;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
// 创建凭证
$credentials = new Credentials(
'YOUR_ACCESS_KEY',
'YOUR_SECRET_KEY'
);
// 创建签名器
$signature = new SignatureV4('s3', 'us-east-1');
// 创建请求
$request = new Request(
'GET',
'https://s3.us-east-1.amazonaws.com/my-bucket/my-object'
);
// 签名请求
$signedRequest = $signature->signRequest($request, $credentials);
// 发送请求
$client = new Client();
$response = $client->send($signedRequest);
echo $response->getBody();
手动实现 Signature V4
如果需要手动实现,以下是核心步骤:
<?php
class AwsSignatureV4
{
private string $accessKey;
private string $secretKey;
private string $region;
private string $service;
public function __construct(
string $accessKey,
string $secretKey,
string $region,
string $service
) {
$this->accessKey = $accessKey;
$this->secretKey = $secretKey;
$this->region = $region;
$this->service = $service;
}
public function sign(
string $method,
string $url,
array $headers = [],
string $body = ''
): array {
$parsedUrl = parse_url($url);
$timestamp = gmdate('Ymd\THis\Z');
$date = substr($timestamp, 0, 8);
// 1. 创建规范请求
$canonicalRequest = $this->createCanonicalRequest(
$method,
$parsedUrl['path'] ?? '/',
$parsedUrl['query'] ?? '',
$headers,
$body
);
// 2. 创建签名字符串
$credentialScope = $date . '/' . $this->region . '/' . $this->service . '/aws4_request';
$stringToSign = "AWS4-HMAC-SHA256\n{$timestamp}\n{$credentialScope}\n" .
hash('sha256', $canonicalRequest);
// 3. 计算签名
$signingKey = $this->getSignatureKey(
$this->secretKey,
$date,
$this->region,
$this->service
);
$signature = hash_hmac('sha256', $stringToSign, $signingKey);
// 4. 添加 Authorization 头
$headers['Authorization'] = sprintf(
'AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request, ' .
'SignedHeaders=%s, Signature=%s',
$this->accessKey,
$date,
$this->region,
$this->service,
$this->getSignedHeaders($headers),
$signature
);
$headers['x-amz-date'] = $timestamp;
return $headers;
}
private function createCanonicalRequest(
string $method,
string $path,
string $query,
array $headers,
string $body
): string {
// 规范 URI
$canonicalUri = $this->normalizePath($path);
// 规范查询字符串
$canonicalQueryString = $this->normalizeQueryString($query);
// 规范头
$canonicalHeaders = $this->getCanonicalHeaders($headers);
$signedHeaders = $this->getSignedHeaders($headers);
// 请求体哈希
$payloadHash = hash('sha256', $body);
return "{$method}\n{$canonicalUri}\n{$canonicalQueryString}\n" .
"{$canonicalHeaders}\n{$signedHeaders}\n{$payloadHash}";
}
private function normalizePath(string $path): string
{
// 对路径进行 URI 编码,但保留正斜杠
$parts = explode('/', $path);
$normalized = [];
foreach ($parts as $part) {
if ($part !== '') {
$normalized[] = rawurlencode(rawurldecode($part));
}
}
return '/' . implode('/', $normalized);
}
private function normalizeQueryString(string $query): string
{
if (empty($query)) {
return '';
}
parse_str($query, $params);
ksort($params);
$pairs = [];
foreach ($params as $key => $value) {
$pairs[] = rawurlencode($key) . '=' . rawurlencode($value);
}
return implode('&', $pairs);
}
private function getCanonicalHeaders(array $headers): string
{
$canonicalHeaders = [];
foreach ($headers as $key => $value) {
$lowerKey = strtolower($key);
$canonicalHeaders[$lowerKey] = $lowerKey . ':' . trim($value);
}
ksort($canonicalHeaders);
return implode("\n", $canonicalHeaders) . "\n";
}
private function getSignedHeaders(array $headers): string
{
$signedHeaders = [];
foreach ($headers as $key => $value) {
$signedHeaders[] = strtolower($key);
}
sort($signedHeaders);
return implode(';', $signedHeaders);
}
private function getSignatureKey(
string $key,
string $dateStamp,
string $regionName,
string $serviceName
): string {
$kDate = hash_hmac('sha256', $dateStamp, 'AWS4' . $key, true);
$kRegion = hash_hmac('sha256', $regionName, $kDate, true);
$kService = hash_hmac('sha256', $serviceName, $kRegion, true);
$kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
return $kSigning;
}
}
// 使用示例
$signer = new AwsSignatureV4(
'YOUR_ACCESS_KEY',
'YOUR_SECRET_KEY',
'us-east-1',
's3'
);
$headers = $signer->sign(
'GET',
'https://s3.us-east-1.amazonaws.com/my-bucket/my-object?versionId=123',
[
'Host' => 's3.us-east-1.amazonaws.com',
]
);
// 使用 cURL 发送请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://s3.us-east-1.amazonaws.com/my-bucket/my-object');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
使用 Composer 包
也可以使用专门的签名包:
composer require aws/aws-sdk-php # 或者轻量级方案 composer require async-aws/core
注意事项
- 时间同步:确保服务器时间与 AWS 服务同步(使用 NTP)
- 安全存储:永远不要硬编码凭证,使用环境变量或 AWS Secrets Manager
- 区域选择:根据实际服务区域配置 'region' 参数
- 服务端点:不同服务有不同的端点和签名规则
调试技巧
// 查看签名过程 echo "Canonical Request:\n$canonicalRequest\n\n"; echo "String to Sign:\n$stringToSign\n\n"; echo "Authorization Header:\n" . $headers['Authorization'] . "\n";
使用 AWS SDK 是最安全和可靠的方式,只有在特殊需求下才建议手动实现签名。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播