PHP生成AWS Signature Version 4签名对接Rekognition失败求助
问题分析与修复方案
你的代码存在几个关键错误,直接导致签名验证失败:
1. 请求URL带多余查询参数
Rekognition的application/x-amz-json-1.1接口不需要在URL中添加?Action=DetectFaces,操作已通过x-amz-target头指定。多余的查询参数会让规范请求的查询字符串部分与Postman生成的不一致,引发签名不匹配。
2. 请求体哈希计算错误
你用http_build_query($data)生成请求体哈希,但Rekognition要求的是JSON格式请求体的SHA256哈希,而非URL编码的键值对。这是核心错误,Postman会正确使用JSON字符串计算哈希,你的代码用了错误格式。
3. 规范请求的查询字符串处理
如果URL无查询参数,规范请求的第三部分应为空字符串,而非parse_url($url, PHP_URL_QUERY)返回的Action=DetectFaces。
修正后的代码
function sign_request($url, $data) { $host = 'rekognition.eu-west-2.amazonaws.com'; $access_key = '<<Acess_Key>>'; $secret_key = '<<Secret_Key>>'; $region = 'eu-west-2'; $service = 'rekognition'; $current = new DateTime('UTC'); $current_date_time = $current->format('Ymd\THis\Z'); $current_date = $current->format('Ymd'); $signed_headers = [ 'content-type' => 'application/x-amz-json-1.1', 'host' => $host, 'x-amz-date' => $current_date_time, 'x-amz-target' => 'RekognitionService.DetectFaces' ]; $signed_headers_string = implode(';', array_keys($signed_headers)); $canonical = [ 'POST', parse_url($url, PHP_URL_PATH), '', // 无查询参数,填空字符串 ]; foreach ($signed_headers as $header => $value) { $canonical[] = "$header:$value"; } $canonical[] = ''; $canonical[] = $signed_headers_string; // 关键修正:将数据转为标准JSON字符串后计算哈希 $json_body = json_encode($data, JSON_UNESCAPED_SLASHES); $canonical[] = hash('sha256', $json_body); $canonical = implode("\n", $canonical); $credential_scope = [$current_date, $region, $service, 'aws4_request']; $key = array_reduce($credential_scope, fn ($key, $credential) => hash_hmac('sha256', $credential, $key, TRUE), 'AWS4' . $secret_key); $credential_scope = implode('/', $credential_scope); $string_to_sign = implode("\n", [ 'AWS4-HMAC-SHA256', $current_date_time, $credential_scope, hash('sha256', $canonical), ]); $signature = hash_hmac('sha256', $string_to_sign, $key); unset($signed_headers['host']); $signed_headers['Authorization'] = "AWS4-HMAC-SHA256 Credential=$access_key/$credential_scope, SignedHeaders=$signed_headers_string, Signature=$signature"; return [ 'headers' => $signed_headers, 'body' => $json_body ]; } // 修正URL,移除多余查询参数 $requestUrl = 'https://rekognition.eu-west-2.amazonaws.com/'; $body = [ "Attributes" => [ "ALL" ], "Image" => [ "Bytes" => "<<BASE64_of_Face_Image>>" ] ]; $result = sign_request($requestUrl, $body ); print_r($result['headers']); // 发送请求时需使用$result['body']作为请求体
额外注意事项
- 确保
json_encode生成的JSON字符串与Postman发送的完全一致(JSON_UNESCAPED_SLASHES避免不必要转义)。 - 验证
x-amz-date格式严格符合YYYYMMDD'T'HHMMSS'Z',DateTime的UTC格式必须准确。 - 签名头部的键名必须全小写,且在规范请求和SignedHeaders中完全一致。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

