使用预签名URL上传S3桶报错:提供的AWS访问密钥不存在
问题解决:预签名URL上传S3报InvalidAccessKeyId错误
核心原因
你手动从EC2元数据获取IAM角色临时凭证时,漏掉了SessionToken。ASIA开头的AccessKeyId是AWS临时凭证,必须配合SessionToken一起使用,缺少它会导致S3验证失败,抛出InvalidAccessKeyId错误。
修复步骤
1. 修改凭证获取函数,补充SessionToken
更新get_aws_creds函数,从元数据中提取并返回SessionToken(注意补上缺失的requests导入):
import requests import json import boto3 import pydantic from botocore.exceptions import ClientError from botocore.client import Config import datetime URL = "http://169.254.169.254/latest/meta-data/iam/security-credentials/my-iam-role-s3" access_key = "" secret_key = "" session_token = "" # 新增全局变量存储SessionToken def get_aws_creds(): global access_key global secret_key global session_token try: response = requests.get(URL) response.raise_for_status() json_response = json.loads(response.text) access_key = json_response["AccessKeyId"] secret_key = json_response["SecretAccessKey"] session_token = json_response["Token"] # 新增:获取SessionToken except requests.exceptions.HTTPError as err: print(err) raise SystemExit(err) return access_key, secret_key, session_token
2. 初始化S3客户端时传入SessionToken
修改get_pre_signed_upload_URL函数,在创建boto3客户端时添加aws_session_token参数:
def get_pre_signed_upload_URL(uuid :str): global access_key global secret_key global session_token if (access_key == "" or secret_key == "" or session_token == ""): access_key, secret_key, session_token = get_aws_creds() s3_client = boto3.client( 's3', aws_access_key_id=access_key, aws_secret_access_key=secret_key, aws_session_token=session_token, # 新增:传入SessionToken region_name="us-west-1", config=Config(signature_version='s3v4')) try: response = s3_client.generate_presigned_post( Bucket=bucket_name, Key=uuid, ExpiresIn=50 ) except ClientError as e: return None return response
3. 验证修复效果
修复后,客户端获取的fields里会新增x-amz-security-token字段,包含SessionToken值,此时再用curl上传就能正常通过S3的验证。
更推荐的优化方案
你完全不需要手动请求元数据获取凭证,boto3会自动从EC2实例的IAM角色中加载临时凭证,代码可以大幅简化,避免手动处理凭证的错误:
import boto3 from botocore.exceptions import ClientError from botocore.client import Config bucket_name = "my-bucket-name-s3" def get_pre_signed_upload_URL(uuid :str): # boto3自动从EC2角色获取凭证,无需手动指定密钥 s3_client = boto3.client( 's3', region_name="us-west-1", config=Config(signature_version='s3v4')) try: response = s3_client.generate_presigned_post( Bucket=bucket_name, Key=uuid, ExpiresIn=50 ) except ClientError as e: return None return response
这种方式会自动处理凭证的轮换,比手动请求元数据更可靠。
内容的提问来源于stack exchange,提问作者Prateek Khatri
相关产品推荐
相关产品推荐

