Angular调用Azure Blob存储PUT接口遇403认证错误求助
解决Angular向Azure Blob存储PUT请求返回403认证失败的问题
我之前也踩过Azure Blob存储认证的坑,结合你的代码来看,几个关键细节没处理对,这才导致了403错误。咱们一步步来梳理和修正:
核心问题分析
你收到的错误提示是签名验证失败,这几乎都是因为Authorization头的签名生成不符合Azure规则,再加上几个请求头的细节错误共同导致的。
1. SharedKey签名不能直接填原始密钥
你当前的Authorization头写的是SharedKey myaccount:<access key>,这是完全错误的。Azure的SharedKey签名需要按照特定规则生成:
- 把请求的方法(PUT)、请求头(尤其是x-ms开头的头)、资源路径等组合成一个签名字符串
- 用存储账户的access key对这个字符串进行HMAC-SHA256加密,再转成Base64编码
- 最后把编码后的结果放到Authorization头里,格式是
SharedKey <账户名>:<生成的签名>
2. x-ms-date必须是当前UTC时间
你写的是固定值2019-04-09,Azure要求这个时间必须是当前UTC标准时间,且和服务器时间差不能超过15分钟,否则会直接拒绝请求。必须动态生成这个值。
3. Content-Length设置错误
你手动指定了1024,但实际请求体"{hel:ds}"的字节数远小于这个值。Content-Length是签名计算的一部分,数值不匹配会导致签名验证失败。要么让HttpClient自动处理这个头,要么准确计算请求体的字节数。
4. 不必要的请求头干扰
Vary是服务器响应头,你作为请求头设置它完全没必要,建议去掉,避免影响签名计算。
修正后的代码示例
我给你整理了可运行的修正代码,这里用crypto-js库来处理签名计算(先通过npm install crypto-js安装):
import { HttpClient, HttpHeaders } from '@angular/common/http'; import * as CryptoJS from 'crypto-js'; // 配置你的存储账户信息 const accountName = 'matasisrael'; const containerName = 'matas'; const accessKey = '你的存储账户access key'; // 注意是存储账户的key,不是SAS const blobUrl = `https://${accountName}.blob.core.windows.net/${containerName}/routes.json`; // 准备请求体 const requestBody = JSON.stringify({ hel: 'ds' }); // 生成当前UTC时间(必须符合格式要求) const currentUtcDate = new Date().toUTCString(); // 计算请求体的字节长度(如果要手动设置Content-Length的话) const contentLength = new TextEncoder().encode(requestBody).length; // 构建签名字符串:注意x-ms开头的头要按字母排序,顺序不能错 const stringToSign = `PUT\n\napplication/json; charset=UTF-8\n${currentUtcDate}\nx-ms-blob-type:BlockBlob\nx-ms-version:2015-02-21\n/${accountName}/${containerName}/routes.json`; // 生成签名:用Base64解析access key,再做HMAC-SHA256加密,最后转Base64 const signature = CryptoJS.HmacSHA256(stringToSign, CryptoJS.enc.Base64.parse(accessKey)).toString(CryptoJS.enc.Base64); // 构建正确的请求头 const httpOptions = { headers: new HttpHeaders({ 'x-ms-version': '2015-02-21', 'x-ms-date': currentUtcDate, 'Content-Type': 'application/json; charset=UTF-8', 'x-ms-blob-type': 'BlockBlob', 'Authorization': `SharedKey ${accountName}:${signature}`, // Content-Length可以省略,HttpClient会自动计算,这里如果加就要准确 'Content-Length': contentLength.toString() }) }; // 发起请求 this.http.put(blobUrl, requestBody, httpOptions).subscribe( data => console.log('Blob上传成功:', data), error => console.error('上传失败:', error) );
更简单的替代方案:使用SAS URL
如果你觉得SharedKey签名太繁琐,强烈推荐用SAS(共享访问签名)。生成带写入权限的SAS token后,直接拼在Blob URL后面,请求时不需要处理Authorization头,代码会简洁很多:
const sasBlobUrl = 'https://matasisrael.blob.core.windows.net/matas/routes.json?<你的SAS Token>'; const httpOptions = { headers: new HttpHeaders({ 'x-ms-version': '2015-02-21', 'x-ms-date': new Date().toUTCString(), 'x-ms-blob-type': 'BlockBlob', 'Content-Type': 'application/json; charset=UTF-8' }) }; this.http.put(sasBlobUrl, requestBody, httpOptions).subscribe( data => console.log('上传成功'), error => console.error('上传失败') );
额外注意事项
- 确保你的存储账户access key或SAS token拥有Blob写入权限(比如SAS要包含
w权限) - 检查容器是否已经存在,私有容器必须通过密钥/SAS才能写入
- 如果使用SAS,注意SAS的有效期,过期后也会返回403
内容的提问来源于stack exchange,提问作者Sagiv Melamed
相关产品推荐
相关产品推荐

