PHP使用SAS Token上传图片至Azure Blob时触发认证格式错误
Azure Blob存储SAS Token上传图片认证错误排查
问题描述
使用PHP系统访问Azure Blob存储,公开模式下可正常上传图片,但使用私有SAS Token时收到如下错误:
InvalidAuthenticationInfoAuthentication information is not given in the correct format. Check the value of Authorization header. RequestId: [xxx]
上传相关PHP代码
function getAccessKey(){ return strval("xxx"); } function getSASForBlob($accountName,$container, $blob, $resourceType, $permissions, $expiry) { $key = getAccessKey(); /* Create the signature */ $_arraysign = array(); $_arraysign[] = $permissions; $_arraysign[] = ''; $_arraysign[] = $expiry; $_arraysign[] = '/' . $accountName . '/' . $container . '/' . $blob; $_arraysign[] = ''; $_arraysign[] = "2014-02-14"; //the API version is now required $_arraysign[] = ''; $_arraysign[] = ''; $_arraysign[] = ''; $_arraysign[] = ''; $_arraysign[] = ''; $_str2sign = implode("\n", $_arraysign); return base64_encode( hash_hmac('sha256', urldecode(utf8_encode($_str2sign)), base64_decode($key), true) ); } function uploadBlob($filepath, $storageAccountname, $containerName, $blobName, $_signature) { $accesskey = getAccessKey(); $extUsed = explode(".",$filepath)[3]; //$filepath = $filepath.'.'.$extUsed; $Date = gmdate('D, d M Y H:i:s \G\M\T'); $handle = fopen($filepath, "r"); $fileLen = filesize($filepath); $blobName = $blobName."." .$extUsed; $URL = "https://$storageAccountname.blob.core.windows.net/$containerName/$blobName"; $expiry = ''; $resourceType = 'b'; $permissions = 'r'; $_parts = array(); $_parts[] = (!empty($expiry))?'se=' . urlencode($expiry):''; $_parts[] = 'sr=' . $resourceType; $_parts[] = (!empty($permissions))?'sp=' . $permissions:''; $_parts[] = 'sig=' . urlencode($_signature); $_parts[] = 'sv=2014-02-14'; $URL = $URL . "?" . implode('&', $_parts); $headerResource = "x-ms-blob-cache-control:max-age=3600\nx-ms-blob-type:BlockBlob\nx-ms-date:$Date\nx-ms-version:2017-11-09"; $urlResource = "/$storageAccountname/$containerName/$blobName"; $extUsed_folded = 'image/'.$extUsed; $arraysign = array(); $arraysign[] = 'PUT'; /*HTTP Verb*/ $arraysign[] = ''; /*Content-Encoding*/ $arraysign[] = ''; /*Content-Language*/ $arraysign[] = $fileLen; /*Content-Length (include value when zero)*/ $arraysign[] = ''; /*Content-MD5*/ $arraysign[] = $extUsed_folded /*'application/pdf'*/; /*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)); // $sig = signWithAccountKey($str2sign, getAccessKey()); $authHeader = "SharedKey $storageAccountname:$sig"; $header_contentType = "image/" .$extUsed; switch($extUsed){ case "jpg": case "jpeg": default: break; case "png": $header_contentType = "image/png"; break; case "tiff": $header_contentType = "image/tiff"; break; } $headers = [ 'Authorization: ' . $authHeader, 'x-ms-blob-cache-control: max-age=3600', 'x-ms-blob-type: BlockBlob', 'x-ms-date: ' . $Date, 'x-ms-version: 2017-11-09', 'Content-Type: '.$header_contentType, 'Content-Length: ' . $fileLen ]; $ch = curl_init(); curl_setopt ($ch, CURLOPT_URL, $URL ); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); curl_setopt($ch, CURLOPT_INFILE, $handle); curl_setopt($ch, CURLOPT_INFILESIZE, $fileLen); curl_setopt($ch, CURLOPT_UPLOAD, true); $result = curl_exec($ch); // echo ('Uploaded successfully<br/>'); // print_r($result); curl_close($ch); $resultsList = array( $URL , $filepath , $result , $blobName, $urlResource , $accesskey ); if (isset($resultsList) && is_array($resultsList)) { return $resultsList; } else { return array(); } }
主调用逻辑
$blobName = 'image-viajes'; $urlAdjunto = ''; $newFilename = '../Imagenes/temp_viajes.jpg'; if (!empty($filename)){ $blobSignature = getSASForBlob('xxx', 'yyy', $blobName, 'b', 'r', ''); echo '<br /><b>BLOB SIGNATURE</b><p>'.$blobSignature.'</p>'; //$blobFotoAdjunta__url = ''; $blobFotoAdjunta = uploadBlob( $newFilename, 'xxx', 'yyy', $blobName, $blobSignature); }
核心问题分析
- 认证方式冲突:使用SAS Token的同时,请求头中还添加了
SharedKey认证头,Azure不允许同时使用两种认证方式,这是触发错误的直接原因。 - SAS权限不足:生成SAS时指定的权限是
r(只读),但上传操作需要w(写入)权限,权限不匹配会导致操作失败。 - API版本不一致:生成SAS用的是
2014-02-14版本,请求头里用的是2017-11-09,版本不一致会引发签名验证问题。 - Blob名称不匹配:
getSASForBlob使用原始$blobName生成签名,但uploadBlob中给Blob添加了后缀,导致签名的资源路径和实际请求路径不一致。
修复步骤
1. 移除SharedKey认证头
在uploadBlob函数中,删除生成$authHeader的代码,同时从$headers数组中移除Authorization相关项,使用SAS Token时无需再用SharedKey认证。
2. 修改SAS权限为写入权限
- 调用
getSASForBlob时,将权限参数从'r'改为'w'(需要读取权限可改为'rw') uploadBlob函数内的$permissions = 'r';同步改为'w'
3. 统一API版本
将生成SAS和请求头的API版本统一为较新版本(如2021-06-08):
getSASForBlob中的版本字符串改为"2021-06-08"uploadBlob中的x-ms-version头改为'2021-06-08',SAS参数里的sv=2014-02-14改为sv=2021-06-08
4. 修正Blob名称匹配问题
在调用getSASForBlob前,先构造好带后缀的完整Blob名称,确保签名路径和实际请求路径一致:
// 主调用逻辑中构造完整Blob名称 $extUsed = explode(".", $newFilename)[3]; $fullBlobName = $blobName . "." . $extUsed; $blobSignature = getSASForBlob('xxx', 'yyy', $fullBlobName, 'b', 'w', $expiry);
5. 补充SAS过期时间
设置合理的过期时间(如1小时),避免空值引发的潜在问题:
$expiry = gmdate('Y-m-d\TH:i:s\Z', time() + 3600);
修复后关键代码片段
修改后的getSASForBlob函数
function getSASForBlob($accountName,$container, $blob, $resourceType, $permissions, $expiry) { $key = getAccessKey(); $_arraysign = array(); $_arraysign[] = $permissions; $_arraysign[] = ''; $_arraysign[] = $expiry; $_arraysign[] = '/' . $accountName . '/' . $container . '/' . $blob; $_arraysign[] = ''; $_arraysign[] = "2021-06-08"; $_arraysign[] = ''; $_arraysign[] = ''; $_arraysign[] = ''; $_arraysign[] = ''; $_arraysign[] = ''; $_str2sign = implode("\n", $_arraysign); return base64_encode( hash_hmac('sha256', urldecode(utf8_encode($_str2sign)), base64_decode($key), true) ); }
修改后的主调用逻辑
$blobName = 'image-viajes'; $urlAdjunto = ''; $newFilename = '../Imagenes/temp_viajes.jpg'; if (!empty($filename)){ $extUsed = explode(".", $newFilename)[3]; $fullBlobName = $blobName . "." . $extUsed; $expiry = gmdate('Y-m-d\TH:i:s\Z', time() + 3600); $blobSignature = getSASForBlob('xxx', 'yyy', $fullBlobName, 'b', 'w', $expiry); echo '<br /><b>BLOB SIGNATURE</b><p>'.$blobSignature.'</p>'; $blobFotoAdjunta = uploadBlob( $newFilename, 'xxx', 'yyy', $blobName, $blobSignature, $expiry); }
内容的提问来源于stack exchange,提问作者NewFactRevealed
相关产品推荐
相关产品推荐

