AWS预签名URL上传文件返回403 SignatureDoesNotMatch问题求助
先给你揪出最可能的核心问题——你生成预签名URL用的是GetObjectRequest,这是用来下载文件的请求类型啊!上传文件得用PutObjectRequest才行,用GET的预签名URL去做PUT上传,AWS肯定直接给你403拒绝了😅
下面是详细的排查方向和修正方案:
1. 修正预签名URL的请求类型(最关键)
你当前的代码是为GetObject(下载)生成预签名URL,上传需要换成PutObjectRequest,修正后的代码如下:
use AsyncAws\S3\S3Client; use AsyncAws\S3\Input\PutObjectRequest; $s3 = new S3Client(); $bucket = 'my-bucket'; $key = 'myfile.pdf'; $date = new \DateTimeImmutable('+15minutes'); $contentType = 'application/pdf'; // 替换为PutObjectRequest用于上传 $input = new PutObjectRequest([ 'Bucket' => $bucket, 'Key' => $key, 'ContentType' => $contentType ]); $presignUrl = $s3->presign($input, $date);
2. 确保请求方法严格匹配
生成PutObject的预签名URL后,Postman里必须选择PUT请求方法,不能用POST/GET等其他方法——这是AWS预签名URL的核心规则:请求方法必须和生成时的请求类型完全一致。
3. Content-Type必须完全一致
生成预签名URL时指定的ContentType(这里是application/pdf),要和Postman请求头里的Content-Type完全匹配:
- 不要额外添加多余字符(比如
application/pdf; charset=utf-8这种带后缀的格式不行) - Postman选择binary文件时可能自动识别Content-Type,要手动检查并修正为和生成时一致的值
4. 检查预签名URL的有效性
- 确认URL没有过期:你设置了15分钟有效期,测试时要确保在生成后的15分钟内发起请求
- 确认URL没有被篡改:复制时不要漏掉任何参数(比如
X-Amz-Signature这类长串参数要完整复制)
5. 验证IAM权限
虽然你用getObject正常,但生成PutObject预签名URL需要对应的权限,确保生成预签名的IAM实体(用户/角色)有s3:PutObject权限,示例策略如下:
{ "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::my-bucket/myfile.pdf" }
6. 检查Bucket的CORS配置(可选)
如果后续要在浏览器端使用这个预签名URL,Bucket的CORS规则需要允许PUT方法;Postman测试时影响不大,但如果你的Bucket CORS完全禁止PUT,也可能触发403。可以临时添加宽松的CORS规则测试:
<CORSConfiguration> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>PUT</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
7. Payload签名校验(特殊场景)
你的预签名URL里包含x-amz-content-sha256=UNSIGNED-PAYLOAD,这意味着AWS不会校验上传文件的哈希值,但如果你的Bucket开启了特定的安全规则(比如要求签名payload),可能需要生成预签名时指定文件的SHA256哈希值。不过这个场景比较少见,先优先排查前面的点。
按照上面的步骤修正后,应该就能正常通过Postman上传文件了。
内容的提问来源于stack exchange,提问作者Guillaume Harari

