使用moto模拟AWS凭证测试S3操作时出现InvalidClientTokenId错误如何解决
问题原因
你代码的报错来自两个配置错误:
- 测试方法上的
@set_initial_no_auth_action_count(0)装饰器要求moto从第一个请求开始就必须校验凭证,但是你调用create_user_with_access_key_and_policy创建IAM用户时,内部的IAM客户端没有传入任何凭证,属于无认证请求,直接被拦截,触发InvalidClientTokenId报错。 - 你的测试用例只加了
@mock_s3装饰器,和create_user_with_access_key_and_policy上的@mock_iam属于两个隔离的mock上下文,IAM生成的凭证无法被S3的mock识别,就算创建用户的步骤通过了,后续S3操作也会校验失败。
解决方案
调整装饰器配置和set_initial_no_auth_action_count的调用时机即可:
- 测试方法同时添加
@mock_iam和@mock_s3装饰器,保证两个服务的mock在同一个上下文内,IAM生成的凭证可以被S3识别。 - 移除测试方法上的
@set_initial_no_auth_action_count(0)装饰器,改为在所有预置操作(创建IAM用户、创建S3桶)完成后再调用该方法,避免预置阶段的无认证请求被拦截。
修改后可运行代码
import os import unittest import boto3 import botocore from moto import mock_s3, mock_iam from botocore.client import ClientError from moto.core import set_initial_no_auth_action_count import json def my_head_bucket(bucket, aws_access_key_id, aws_secret_access_key): """ 示例函数,实际场景下会包含更多S3操作,从其他模块导入,不需要添加moto相关装饰器 """ s3_client = boto3.client("s3", aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key) s3_client.head_bucket(Bucket=bucket) def create_user_with_access_key_and_policy(user_name="test-user"): """ 创建带S3全权限的IAM用户,返回访问密钥 """ policy_document = { "Version": "2012-10-17", "Statement": [ {"Effect": "Allow", "Action": "s3:*", "Resource": "*"} ], } # 创建IAM客户端和用户 client = boto3.client("iam", region_name="us-east-1") client.create_user(UserName=user_name) # 创建并挂载权限策略 policy_arn = client.create_policy( PolicyName="policy1", PolicyDocument=json.dumps(policy_document) )["Policy"]["Arn"] client.attach_user_policy(UserName=user_name, PolicyArn=policy_arn) # 返回访问密钥 return client.create_access_key(UserName=user_name)["AccessKey"] class TestMyTest(unittest.TestCase): @mock_iam @mock_s3 def test_correct_credentials(self): """测试合法凭证可以正常执行操作""" # 预置资源:创建IAM用户、S3桶 iam_keys = create_user_with_access_key_and_policy() s3 = boto3.client('s3', aws_access_key_id=iam_keys["AccessKeyId"],aws_secret_access_key=iam_keys["SecretAccessKey"]) s3.create_bucket(Bucket='mock_bucket') # 预置完成,开启强制凭证校验 set_initial_no_auth_action_count(0) # 执行测试逻辑,预期无报错 my_head_bucket('mock_bucket', aws_access_key_id=iam_keys["AccessKeyId"],aws_secret_access_key=iam_keys["SecretAccessKey"]) @mock_iam @mock_s3 def test_incorrect_credentials(self): """测试非法密钥会抛出预期异常""" # 预置资源:创建IAM用户、S3桶 iam_keys = create_user_with_access_key_and_policy() s3 = boto3.client('s3', aws_access_key_id=iam_keys["AccessKeyId"],aws_secret_access_key=iam_keys["SecretAccessKey"]) s3.create_bucket(Bucket='mock_bucket') # 预置完成,开启强制凭证校验 set_initial_no_auth_action_count(0) # 执行测试逻辑,预期抛出ClientError with self.assertRaises(ClientError): my_head_bucket('mock_bucket', aws_access_key_id=iam_keys["AccessKeyId"],aws_secret_access_key="invalid") if __name__ == '__main__': unittest.main()
内容的提问来源于stack exchange,提问作者cyau
相关产品推荐
相关产品推荐

