Lambda调用DynamoDB PutItem报AccessDeniedException权限错误排查
你的代码根本没有使用Cognito联邦身份对应的临时凭证,报错已经把问题说的很清楚了:当前调用DynamoDB用的身份是Lambda自带的执行角色CreateProfileFunctionRole-1VOW05TI1WR20,不是你给Cognito用户组关联的那个角色。你给Cognito角色附加AdministratorAccess完全加错了对象,当前做权限校验的是Lambda执行角色,它没有DynamoDB表的写权限。
你写的boto3.Session(region_name='eu-west-2')没有传入任何自定义凭证,boto3会按照默认凭证链读取Lambda运行环境内置的执行角色凭证,完全感知不到你Cognito用户登录拿到的身份信息。你用jwt解析id_token能看到角色关联信息,不代表这个角色的凭证会自动被boto3使用——id_token只是身份声明,调用AWS服务必须用对应的临时IAM凭证三件套(AccessKey、SecretKey、SessionToken),你不手动传入的话,代码不会自动用这个身份。
根据你实际的权限设计选一种就行:
方案1:用Lambda执行角色访问DynamoDB(最常用的服务端代理模式)
这种模式下不需要让用户直接有DynamoDB权限,所有DynamoDB操作都由Lambda代执行,你只需要:
- 打开IAM控制台,找到报错里提到的Lambda执行角色
arn:aws:sts::<ACCOUNT_ID>:assumed-role/<PREFIX>-CreateProfileFunctionRole-1VOW05TI1WR20 - 给这个角色附加DynamoDB的PutItem权限(或者你要加AdministratorAccess也直接加在这个角色上,别加给Cognito角色)
- 代码里不需要手动建Session,直接用默认客户端就行,Lambda会自动管理凭证轮转:
import boto3 import json import uuid # 初始化客户端放函数外,复用连接提升性能 ddb_client = boto3.client('dynamodb', region_name='eu-west-2') def create_profile(event, context): profile = json.loads(event["body"]) row_id = str(uuid.uuid4()) item = { 'RowID': {'S': row_id}, 'first_name': {'S': profile['first_name']}, 'last_name': {'S': profile['last_name']} } ddb_client.put_item(TableName='Persons', Item=item)
方案2:透传Cognito用户身份,用用户的联邦凭证访问DynamoDB
如果你需要严格以登录Cognito用户的身份做DynamoDB权限控制,那你需要手动从API Gateway透传的事件里拿到Cognito联邦身份对应的临时凭证,传入boto3 Session:
- 首先确认你的API Gateway授权配置正确,会把Cognito Identity Pool分发的临时凭证三件套透传到Lambda的event上下文里,一般在
event['requestContext']['authorizer']路径下 - 修改代码,用拿到的用户凭证初始化boto3会话:
import boto3 import json import uuid def create_profile(event, context): profile = json.loads(event["body"]) # 从API Gateway授权上下文中提取Cognito联邦身份的临时凭证,字段路径根据你实际的API配置调整 auth_ctx = event['requestContext']['authorizer'] user_session = boto3.Session( aws_access_key_id=auth_ctx['accessKey'], aws_secret_access_key=auth_ctx['secretKey'], aws_session_token=auth_ctx['sessionToken'], region_name='eu-west-2' ) ddb_client = user_session.client('dynamodb') row_id = str(uuid.uuid4()) item = { 'RowID': {'S': row_id}, 'first_name': {'S': profile['first_name']}, 'last_name': {'S': profile['last_name']} } ddb_client.put_item(TableName='Persons', Item=item)
这种模式下你给Cognito用户组关联角色加的DynamoDB权限才会生效。
补充排查点:如果你确认已经给对应角色加了权限还是报同样的错,先检查策略里的资源ARN是不是和你实际的表完全匹配,有没有写错区域、表名、账号ID,有没有存在策略条件限制。
内容的提问来源于stack exchange,提问作者Sandeep

