S3预签名上传URL未到过期时间却提示已过期问题排查
问题分析与解决方案
从错误信息来看,预签名URL的过期时间(2022-09-07T04:14:50Z)远早于S3服务器当前时间(2022-09-13T06:40:18Z),同时你设置的有效期是10秒,但错误中X-Amz-Expires显示为5秒,这指向几个明确的问题:
核心原因1:Lambda运行环境时间与AWS S3服务器时间严重不同步
预签名URL的有效期是基于生成时的Lambda本地时间计算的,如果Lambda的系统时间比S3服务器时间慢了数天,那么生成的URL在S3看来从一开始就已经过期。
解决方法:
- 在Lambda代码中添加日志,打印当前时间:
对比S3返回的fmt.Println("Lambda current time:", time.Now().UTC())ServerTime,确认时间是否同步。AWS Lambda默认运行环境时间是UTC且与NTP同步,若你使用了自定义运行时或修改过时间配置,需修复时间同步问题。
核心原因2:预签名请求方法与前端上传方法不匹配
你通过PutObjectRequest生成的是PUT请求的预签名URL,但前端使用POST请求+FormData上传,这会导致S3的签名验证失败,可能表现为"请求过期"这类误导性错误。
解决方法:
方案A:修改前端使用PUT方法上传
将前端代码中的请求方法改为PUT,直接发送文件内容而非FormData:
try { const { data: { fileUploadUrl }, } = await graphQlClient.query({ query: gql(getFileUploadUrlQuery), variables: { filename }, }); const ajaxRequest = new XMLHttpRequest(); ajaxRequest.upload.addEventListener("progress", progressHandler, false); ajaxRequest.addEventListener("load", completeHandler, false); ajaxRequest.addEventListener("error", errorHandler, false); ajaxRequest.addEventListener("abort", abortHandler, false); // 改为PUT方法 ajaxRequest.open("PUT", fileUploadUrl); // 设置Content-Type与预签名时一致 ajaxRequest.setRequestHeader("Content-Type", file.type); // 直接发送文件,而非FormData ajaxRequest.send(file); } catch (err) { console.log(err); }
方案B:后端生成POST表单的预签名
如果你需要保留POST+FormData的上传方式,需改用S3的POST上传预签名机制,示例代码如下:
// 生成POST上传表单参数 postInput := &s3.PostPolicyInput{ Bucket: aws.String(awssettings.FileStorageBucketName), Expires: aws.Time(time.Now().Add(10 * time.Second)), Conditions: [][]interface{}{ []interface{}{"eq", "$key", fmt.Sprintf("%s/%s", *cognitoUser.Username, filename)}, []interface{}{"eq", "$Content-Type", uploadContentType}, []interface{}{"content-length-range", 0, 10485760}, // 限制文件大小,按需调整 }, } postPolicy, err := s3Session.PostPolicy(postInput) if err != nil { // 处理错误 } // 获取表单字段和POST URL formFields := postPolicy.FormFields postUrl := fmt.Sprintf("https://%s.s3.amazonaws.com", awssettings.FileStorageBucketName)
前端需使用这些表单字段构建FormData,再发送POST请求到postUrl。
额外检查点
确认
urlExpiresAt设置是否生效:在Lambda中打印该值,以及计算出的过期时间:fmt.Println("Expires duration:", urlExpiresAt) fmt.Println("Calculated expires time:", time.Now().Add(urlExpiresAt).UTC())若输出的过期时间与错误中的
Expires字段不符,检查是否有其他代码逻辑修改了有效期设置。移除不必要的公开存储桶策略:预签名URL本身已包含上传权限,无需设置
Principal: "*"的公开策略,该策略可能引发权限混淆,建议暂时移除,仅保留预签名机制验证上传。
内容的提问来源于stack exchange,提问作者Andrew Kovalenko
相关产品推荐
相关产品推荐

