部署后使用AWS S3预签名POST URL上传图片提示InvalidAccessKeyId错误,本地测试正常
兄弟,我刚好碰到过一模一样的问题!咱们先把问题根儿找清楚:你本地用的是长期IAM访问密钥(AKIA开头),但部署到Lambda之后,函数用的是IAM角色给的临时凭证(ASIA开头)。临时凭证必须带X-Amz-Security-Token才能被S3认,但你的代码里没把这个token加到预签名POST的表单字段里,这就导致了权限验证失败。
为啥DynamoDB能正常跑?
这是因为AWS SDK对DynamoDB这类客户端特别友好——它会自动从Lambda的执行环境里捞到完整的临时凭证(包括session token),请求的时候自动带上。但createPresignedPost这个方法比较“直男”,不会自动把session token塞进POST的表单里,得咱们手动加。
具体怎么改?
给预签名POST加Session Token
修改你的createUrl函数,在Fields里加上X-Amz-Security-Token——Lambda环境会自动把这个值放到process.env.AWS_SESSION_TOKEN里,咱们直接拿就行。改完的代码是这样的:
public createUrl(filename: string): Promise<any> { // 这里注意返回类型,原先是string但实际返回的是带url和fields的对象 return new Promise((resolve, reject) => { const postParams = { Bucket: "{{bucket-name}}", Fields: { key: filename, }, Conditions: [ ["content-length-range", 0, 1048576], ["starts-with", "$Content-Type", "image/"], ], Expires: 120, }; // 有临时token就加上,本地测试的时候这个变量不存在,不会影响 if (process.env.AWS_SESSION_TOKEN) { postParams.Fields['X-Amz-Security-Token'] = process.env.AWS_SESSION_TOKEN; } this.s3.createPresignedPost(postParams, (error, data) => { if (error) reject(error); else resolve(data); }); }); }对了,你原来的函数返回类型标了
Promise<string>,但createPresignedPost返回的是包含url和fields的对象,这个得改一下,不然TypeScript会报错。检查IAM角色和桶的CORS
先确认你的Lambda执行角色有s3:PutObject权限(针对目标存储桶)。另外,桶的CORS配置得允许你的前端域名发POST请求,比如:<CORSConfiguration> <CORSRule> <AllowedOrigin>https://你的前端域名.com</AllowedOrigin> <AllowedMethod>POST</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>验证返回结果
部署完再调用接口,看看返回的fields里有没有X-Amz-Security-Token字段。有的话,前端上传的时候把这个字段和其他表单数据一起提交给S3的URL就行。
补充一句
长期密钥(AKIA)不需要session token,所以本地测啥问题没有;但临时凭证(ASIA)必须带这个token,S3才认。而且AWS SDK的createPresignedUrl(GET请求用的)会自动处理token,但createPresignedPost就得手动加,这坑我当初踩了好久!
内容的提问来源于stack exchange,提问作者matikowy

