使用S3预签名POST上传文件时遭遇AccessDenied错误排查
问题概述
通过Lambda生成S3预签名POST信息,在Postman中构造表单上传文件时持续收到403 AccessDenied错误。已配置Lambda权限(含PutObject、多部分上传相关权限)、关闭桶的公共访问拦截、设置宽松CORS策略,但问题仍存在。
可能的原因与解决方法
1. Key参数未替换为实际文件名
生成预签名POST时使用了Key="${filename}"作为占位符,但上传时如果未将key字段的值替换为真实的文件名,会导致请求与Policy中的条件不匹配,触发AccessDenied。
解决:
- 在Postman中,将
key字段的值从${filename}改为实际要上传的文件名(如test.jpg); - 若需支持动态文件名,修改生成代码的条件为
starts-with规则,允许任意Key前缀:conditions = [ {"acl": "private"}, ["starts-with", "$key", ""] ]
2. Postman表单字段遗漏或错误
预签名POST返回的fields中的所有参数(acl、key、AWSAccessKeyId、x-amz-security-token、policy、signature)必须全部作为表单字段添加,且字段名需完全匹配(区分大小写)。
检查点:
- 确认所有字段都已添加,无遗漏;
- 确保
x-amz-security-token字段完整复制(STS临时凭证较长,容易截断); - 字段值无多余空格或格式错误。
3. Policy文档条件不匹配
预签名POST的policy是Base64编码的JSON文档,包含上传的权限和条件。若实际请求参数与Policy中的条件不符,会直接被拒绝。
调试方法:
- 将返回的
policy值进行Base64解码,查看其中的expiration(是否已过期)和conditions(是否与上传参数匹配); - 示例解码后的Policy:
若{ "expiration": "2024-05-20T15:30:00Z", "conditions": [ {"acl": "private"}, {"bucket": "my-bucket"}, ["eq", "$key", "${filename}"] ] }key条件是固定的${filename},但上传时未替换,就会触发不匹配。
4. 临时凭证有效性问题
Lambda使用的是STS临时凭证(包含x-amz-security-token),若凭证已过期,或传递时出现截断/错误,会导致权限验证失败。
检查点:
- 确认生成预签名POST后尽快测试,避免凭证过期(默认Lambda临时凭证有效期为1小时);
- 完整复制
x-amz-security-token的值,无遗漏字符。
关键调试步骤
开启S3访问日志
为S3桶开启访问日志,日志中会包含具体的错误代码(如InvalidPolicyDocument、SignatureDoesNotMatch)和详细原因,是定位问题最有效的方式。简化测试场景
暂时去掉acl字段和对应的条件,生成最简版本的预签名POST,测试是否能成功上传。若成功,再逐步添加条件排查冲突点:response = s3_client.generate_presigned_post( Bucket='my-bucket', Key="test.jpg" # 使用固定文件名 )验证请求签名
确保Postman中的所有表单参数都参与了签名计算,不要添加额外的自定义头或参数,避免破坏签名验证。
内容的提问来源于stack exchange,提问作者Wintermute

