React使用Python Lambda生成的S3预签名URL上传图片遇403错误
问题排查方案
关于你提到的公共访问和ACL疑问
关闭S3公共访问完全不影响预签名URL的使用,无需调整公共访问配置。ACL默认设置private即可,不需要开启public-read,预签名URL的权限完全来源于生成它的身份(也就是Lambda的执行角色)的权限,和桶的公开权限无关。
具体排查步骤
第一步:检查Lambda执行角色的IAM权限
Lambda使用的执行角色必须配置s3:PutObject权限,且资源路径要正确指向桶内对象,参考策略示例:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::你的S3桶名称/*" } ] }注意资源路径末尾必须加
/*,仅写桶ARN会导致权限不生效。第二步:检查S3桶的CORS配置
你的报错类型为cors,大概率是CORS规则配置错误,S3桶需要放开你前端域名的PUT请求权限,参考配置:[ { "AllowedHeaders": ["*"], "AllowedMethods": ["PUT", "GET"], "AllowedOrigins": ["你的前端域名,开发环境可填http://localhost:3000"], "ExposeHeaders": ["ETag"] } ]第三步:校验Content-Type是否完全一致
生成预签名URL时传入的ContentType参数,必须和前端PUT请求头里的Content-Type完全一致,包括大小写、额外参数,比如后端传的是image/jpeg就不能在前端写成image/JPEG,也不能多带charset=utf-8这类额外参数,建议前后端都打印日志确认两个值完全匹配。第四步:检查S3桶的额外限制配置
如果你的桶开了服务端加密(SSE),生成预签名URL时需要补充对应的加密参数,比如使用SSE-S3加密的话,要在generate_presigned_url的参数里添加"ServerSideEncryption": "AES256";如果桶配置了自定义桶策略,检查是否存在限制Lambda角色访问、要求特定请求头的规则。第五步:确认区域和签名配置
boto3初始化S3客户端时要指定和桶一致的区域,否则会出现签名不匹配的问题,示例:s3_client = boto3.client("s3", region_name="你的桶所在区域,比如ap-northeast-1")另外不要缓存预签名URL太久,你配置的300秒有效期是合理的,拿到URL后要尽快发起请求。
快速验证方法
你可以先在本地用AWS CLI生成预签名URL,用curl测试能否上传,如果测试成功说明IAM和桶配置没问题,问题出在前后端参数匹配环节;如果测试失败就优先排查IAM和桶配置。
内容的提问来源于stack exchange,提问作者Aaron Li

