PHP+ cURL向Azure表存储插入JSON遇认证错误求排查
问题排查与修复方案
你的代码核心问题是复用了Blob存储的签名逻辑和请求头,但表存储的REST API要求不同,导致签名验证失败。以下是具体问题和修复步骤:
1. 移除Blob存储专属请求头
代码中添加了x-ms-blob-cache-control和x-ms-blob-type,这两个是Blob存储特有的头部,表存储API不需要。这些多余的头部会被纳入签名计算,导致签名与服务器预期不匹配。
2. 修正CanonicalizedHeaders(规范化头部)
签名计算时,规范化头部只需要包含表存储请求实际使用的x-ms-开头的头部,即x-ms-date和x-ms-version,且格式必须严格遵循:
- 头部名称转为小写
- 按字母顺序排序
- 格式为
头部名称:值,每个头部单独一行
3. 调整签名构造数组
移除与Blob相关的头部后,签名构造数组中的$headerResource需要更新,同时确保所有字段符合表存储API的要求。
修复后的完整代码
function sendTableEntries($jsonData, $storageAccount, $tablename, $destinationURL, $accesskey) { $currentDate = gmdate("D, d M Y H:i:s T", time()); $contLen = strlen($jsonData); // 仅保留表存储需要的x-ms头部,小写并排序 $headerResource = "x-ms-date:$currentDate\nx-ms-version:2017-07-29"; $urlResource = "/$storageAccount/$tablename"; $arraysign = array(); $arraysign[] = 'POST'; /* HTTP Verb */ $arraysign[] = ''; /* Content-Encoding */ $arraysign[] = ''; /* Content-Language */ $arraysign[] = $contLen; /* Content-Length */ $arraysign[] = ''; /* Content-MD5 */ $arraysign[] = 'application/json'; /* Content-Type */ $arraysign[] = ''; /* Date */ $arraysign[] = ''; /* If-Modified-Since */ $arraysign[] = ''; /* If-Match */ $arraysign[] = ''; /* If-None-Match */ $arraysign[] = ''; /* If-Unmodified-Since */ $arraysign[] = ''; /* Range */ $arraysign[] = $headerResource; /* CanonicalizedHeaders */ $arraysign[] = $urlResource; /* CanonicalizedResource */ $str2sign = implode("\n", $arraysign); $sig = base64_encode(hash_hmac('sha256', urldecode(utf8_encode($str2sign)), base64_decode($accesskey), true)); $authHeader = "SharedKey $storageAccount:$sig"; // 移除Blob专属头部,保留表存储必要头部 $headers = array( 'Authorization: ' . $authHeader, 'x-ms-date: ' . $currentDate, 'x-ms-version: 2017-07-29', 'Content-Type: application/json', 'Content-Length: ' . $contLen ); $ch = curl_init($destinationURL); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSLVERSION, 6); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); // 直接用POST而非CUSTOMREQUEST curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); $result = curl_exec($ch); echo 'Result<br/>'; print_r($result); echo 'Error<br/>'; print_r(curl_error($ch)); curl_close($ch); } $accesskey = "ACCESSKEY"; $storageAccount = 'storageaccount'; $tablename = 'tablename'; $temp = array(); $temp["PartitionKey"] = "5432"; $temp["RowKey"] = "234235"; $temp["value"] = 3455; $temp["valid"] = 1; $content = json_encode($temp); $destinationTable = "https://$storageAccount.table.core.windows.net/$tablename"; sendTableEntries($content, $storageAccount, $tablename, $destinationTable, $accesskey);
额外注意事项
- 确保
$accesskey是存储账户的原始密钥(不是连接字符串),且没有额外空格或换行。 - 若JSON包含非ASCII字符,建议用
mb_strlen($jsonData, 'UTF-8')计算Content-Length,避免字节数计算错误。 - 表存储要求每个实体必须包含
PartitionKey和RowKey,你的代码已经满足这一点,无需修改。
内容的提问来源于stack exchange,提问作者Justin Nafe
相关产品推荐
相关产品推荐

