React Native上传阿里云OSS时预签名URL签名不匹配问题
React Native上传阿里云OSS出现SignatureDoesNotMatch错误排查与解决
问题背景
React Native 0.70.x应用上传图片到阿里云OSS前,先从Node.js/Express 20.x后端获取预签名URL。后端根据OSS文件名(作为object key)、内容类型(image/jpeg)和PUT请求方法生成预签名URL,但前端使用该URL发起PUT请求时,OSS持续返回403签名不匹配错误。
相关代码片段
后端生成预签名URL代码(存在语法错误)
// 注意:原代码存在语法错误,多了一个右括号 sUrl = store.signatureUrl(items[i][0]), { expires: parseInt(process.env.OSSExpireIn), // 有效期3600s method: 'PUT', 'Content-Type': _type // _type为image/jpeg };
后端OSS存储实例定义
store: function() { try { let bucket_name = 'oss-1', region_name='oss-cn-xxxx'; let store = OSS({ accessKeyId: process.env.AliStsAccessKeyId, accessKeySecret: process.env.AliStsAccessKeySecret, bucket: bucket_name, region: region_name }); return store; } catch(err) { console.log("Error creating store with OSS", err); return false; }; },
前端上传代码(存在上传方式错误)
fileData = await ReactNativeBlobUtil.fs.readFile(filePath, 'base64'); const formData = new FormData(); formData.append('file', fileData, {type:contentTypeMap[extension]}); const response = await fetch(preSignedUrl, { method: 'PUT', body: formData, });
错误响应信息
前端控制台403响应
LOG respnose in uploadtooss : {"_bodyBlob": {"_data": {"__collector": [Object], "blobId": "f0071d9c-f218-4b26-bcaf-0d7e8983bbf3", "offset": 0, "size": 1127}}, "_bodyInit": {"_data": {"__collector": [Object], "blobId": "f0071d9c-f218-4b26-bcaf-0d7e8983bbf3", "offset": 0, "size": 1127}}, "bodyUsed": false, "headers": {"map": {"connection": "keep-alive", "content-length": "1127", "content-type": "application/xml", "date": "Thu, 08 Feb 2024 22:18:27 GMT", "server": "AliyunOSS", "x-oss-ec": "0002-00000040", "x-oss-request-id": "65C5533307D4B93635D7511C", "x-oss-server-time": "95"}}, "ok": false, "status": 403, "statusText": "", "type": "default", "url": "https://oss-hz-1.oss-cn-xxxxxx.aliyuncs.com/22790-fCWOVB_1000000021.jpg?OSSAccessKeyId=LTAI5t5axvqwo2qGnxxxxxxxx&Expires=1707432506&Signature=m60o9WszMIcfzF8mHWQ2HDPHsHW%3D"}
Postman测试返回的XML错误
<?xml version="1.0" encoding="UTF-8"?> <Error> <Code>SignatureDoesNotMatch</Code> <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message> <RequestId>646DCB189AE2D1333018****</RequestId> <HostId>bucket.oss-cn-xxxxx.aliyuncs.com</HostId> <OSSAccessKeyId>LTAI5tMw7e8syBazfpSS****</OSSAccessKeyId> <SignatureProvided>tPN3MChhvKk2XixolQLonoJW****</SignatureProvided> <StringToSign>PUT\n\n\nTue, 23 May 2023 15:24:55 GMT\n/bucket/?acl</StringToSign> <StringToSignBytes>50 55 54 0A 0A 0A 54 75 65 2C 20 32 33 20 4D 61 79 20 32 30 32 33 20 31 35 3A 32 34 3A 35 35 20 47 4D 54 0A 2F 64 69 6E 61 72 79 2F 3F 61 63 6C </StringToSignBytes> <EC>0002-00000040</EC> </Error>
错误原因分析
- 后端代码语法错误:生成签名时的参数传递存在语法错误,多了一个右括号,导致
Content-Type等参数未被正确传入签名计算逻辑。 - 前端上传方式错误:使用
FormData作为PUT请求体不符合OSS预签名PUT的要求,OSS要求直接上传文件二进制内容,FormData会改变请求的Content-Type和请求体结构,导致签名校验不通过。 - Postman测试操作错误:从返回的
StringToSign可以看出,测试时请求路径被错误附加了?acl参数,和预签名时的object key不匹配。
解决方案
1. 修复后端签名生成代码
修正语法错误,确保参数正确传递给signatureUrl方法:
sUrl = store.signatureUrl(items[i][0], { expires: parseInt(process.env.OSSExpireIn), method: 'PUT', 'Content-Type': _type });
2. 修改前端上传逻辑
直接上传文件二进制Blob,确保请求Content-Type与签名时完全一致:
// 读取文件为Blob格式 const blob = await ReactNativeBlobUtil.fs.readFile(filePath, 'blob'); const response = await fetch(preSignedUrl, { method: 'PUT', headers: { 'Content-Type': 'image/jpeg' // 必须和签名时指定的Content-Type完全一致 }, body: blob // 直接传递二进制Blob,而非FormData });
3. 规范Postman测试流程
- 选择PUT请求,将图片以binary模式作为请求体上传
- 使用正确的预签名URL,不要额外添加
?acl等无关参数 - 请求头中指定
Content-Type: image/jpeg,与签名时保持一致
额外检查点
- 确认后端生成签名时的object key与前端上传的目标路径完全一致,无多余字符或斜杠差异
- 检查服务器时间与OSS服务器时间是否同步,时间差过大可能导致签名失效
- 验证STS临时密钥是否具备PUT操作权限,bucket和region配置是否正确
内容的提问来源于stack exchange,提问作者user938363
相关产品推荐
相关产品推荐

