通过API Gateway+Lambda访问DynamoDB时如何配置Fine-Grained Access Control?
疑问1:用户不直接访问DynamoDB时,是否无法使用Fine-Grained Access Control?
完全可以使用细粒度访问控制,只是你的访问路径是通过Lambda代理,不能直接套用用户直连DynamoDB的配置方案,需要调整身份传递和权限配置的逻辑。
疑问2:若可以使用,遗漏了哪些配置?
你遗漏了3个核心配置环节:
- Lambda执行角色的权限逻辑错误:
EndUser-Role的策略是针对Cognito身份池用户的,Lambda直接用该角色执行时,身份是Lambda的假设角色ARN,而非用户的Cognito身份,导致策略中的条件判断不通过 - 未将用户的Cognito身份信息正确传递到Lambda并用于DynamoDB请求校验
- 策略中的条件配置未匹配Lambda代理访问的场景
具体解决方案(两种可选方案)
方案一:基于IAM细粒度权限+Cognito临时凭证(推荐,符合最小权限原则)
该方案让Lambda使用用户的Cognito身份获取临时凭证访问DynamoDB,确保权限严格绑定到终端用户:
1. 修正EndUser-Role的IAM策略
移除多余的ForAllValues:前缀(仅需匹配单个用户sub),并添加临时凭证相关权限:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query" ], "Resource": "arn:aws:dynamodb:us-west-2:123456789111:table/lists", "Condition": { "StringEquals": { "dynamodb:LeadingKeys": "${cognito-identity.amazonaws.com:sub}" } } }, { "Effect": "Allow", "Action": "sts:AssumeRoleWithWebIdentity", "Resource": "arn:aws:iam::123456789111:role/EndUser-Role" } ] }
2. 配置API Gateway传递用户身份
在API Gateway的Cognito Authorizer设置中:
- 勾选「Invoke with caller credentials」,确保用户的
sub(Cognito唯一标识)和identityId(身份池ID)被传递到Lambda事件中 - 或通过集成请求的映射模板,将
$context.authorizer.claims.sub写入Lambda的输入事件
3. 修改Lambda代码,使用用户身份访问DynamoDB
在Lambda中通过Cognito身份池获取用户临时凭证,再初始化DynamoDB客户端:
import boto3 from botocore.credentials import Credentials def lambda_handler(event, context): # 从API Gateway上下文提取用户身份信息 user_sub = event['requestContext']['authorizer']['claims']['sub'] identity_id = event['requestContext']['authorizer']['claims']['cognito:identity_id'] # 获取用户临时凭证 cognito_identity = boto3.client('cognito-identity') cred_response = cognito_identity.get_credentials_for_identity(IdentityId=identity_id) temp_creds = Credentials( access_key=cred_response['Credentials']['AccessKeyId'], secret_key=cred_response['Credentials']['SecretKey'], token=cred_response['Credentials']['SessionToken'] ) # 使用用户凭证操作DynamoDB dynamodb = boto3.client('dynamodb', credentials=temp_creds, region_name='us-west-2') list_name = event['queryStringParameters']['listName'] try: db_response = dynamodb.get_item( TableName='lists', Key={'userId': {'S': user_sub}, 'listName': {'S': list_name}} ) return {'statusCode': 200, 'body': db_response.get('Item', {})} except Exception as e: return {'statusCode': 500, 'body': str(e)}
4. 配置Lambda执行角色权限
给Lambda执行角色添加cognito-identity:GetCredentialsForIdentity权限,无需直接配置DynamoDB权限(操作权限由用户临时凭证提供)。
方案二:应用层数据过滤(简单易实现,依赖代码逻辑)
若不想使用临时凭证,可在Lambda代码中强制过滤用户数据,同时限制Lambda的DynamoDB权限:
1. 配置Lambda执行角色的IAM策略
给Lambda执行角色配置最小DynamoDB权限:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query" ], "Resource": "arn:aws:dynamodb:us-west-2:123456789111:table/lists" } ] }
2. 修改Lambda代码,强制过滤用户数据
在代码中提取用户sub,仅查询当前用户的数据:
import boto3 dynamodb = boto3.resource('dynamodb', region_name='us-west-2') table = dynamodb.Table('lists') def lambda_handler(event, context): user_sub = event['requestContext']['authorizer']['claims']['sub'] list_name = event['queryStringParameters']['listName'] try: response = table.get_item( Key={'userId': user_sub, 'listName': list_name} ) return {'statusCode': 200, 'body': response.get('Item', {})} except Exception as e: return {'statusCode': 500, 'body': str(e)}
502错误原因说明
当你将Lambda执行角色换成EndUser-Role时,权限被拒绝的核心原因是:
策略中的${cognito-identity.amazonaws.com:sub}是Cognito身份池用户的专属变量,但Lambda执行时使用的是角色ARN(arn:aws:sts::123456789111:assumed-role/EndUser-role/list-lambda),该身份没有cognito-identity.amazonaws.com:sub属性,导致IAM条件判断不通过,最终拒绝访问请求。
内容的提问来源于stack exchange,提问作者thedeepfield

