You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 14:50:49