本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
授权和身份验证
本节介绍用于配置应用程序的安全性和数据保护的选项。
授权类型
您可以通过五种方式授权应用程序与您的Amazon AppSync GraphQL API 进行交互。您通过在 Amazon AppSync API 或 CLI 调用中指定以下授权类型值之一,以指定您使用哪种授权类型:
-
-
API_KEY
-
适用于使用 API 密钥。
-
-
-
AWS_LAMBDA
-
用于使用Amazon Lambda函数。
-
-
-
AWS_IAM
-
用于使用Amazon Identity and Access Management (IAM
) 权限。
-
-
-
OPENID_CONNECT
-
适用于使用 OpenID Connect 提供商。
-
-
-
AMAZON_COGNITO_USER_POOLS
-
用于使用 Amazon Cognito 用户池。
-
这些基本授权类型适用于大多数开发人员。对于更高级的用例,您可以通过控制台、CLI 和添加其他授权模式Amazon CloudFormation。对于其他授权模式,Amazon AppSync 提供采用上面列出的值(即、、API_KEY
AWS_LAMBDA
AWS_IAM
OPENID_CONNECT
、和AMAZON_COGNITO_USER_POOLS
)的授权类型。
当您指定API_KEY
AWS_LAMBDA
、或AWS_IAM
作为主授权类型或默认授权类型时,您无法再次将其指定为其他授权模式之一。同样,你不能在其他授权模式下复制API_KEY
,AWS_LAMBDA
也不能在其他授权模式AWS_IAM
内复制。你可以使用多个 Amazon Cognito 用户池和 OpenID Connect 提供商。但是,您不能在默认授权模式和任何其他授权模式之间使用重复的 Amazon Cognito 用户池或 OpenID Connect 提供商。您可以使用相应的配置正则表达式为 Amazon Cognito 用户池或 OpenID Connect 提供商指定不同的客户端。
API_KEY 授权
未经身份验证的 API 比经过身份验证的 API 需要更严格的限制。一种对未经身份验证的 GraphQL 终端节点的限制进行控制的方法是使用 API 密钥。API 密钥是应用程序中的一个硬编码值,当您创建未经身份验证的 GraphQL 终端节点时由 Amazon AppSync 服务生成。您可以从控制台、CLI 或 API 参考中轮换Amazon AppSync API 密钥。
API 密钥可配置为最多 365 天,并且您可以将现有到期日期再延长多达 365 天(从到期日期当天开始)。建议将 API 密钥用于开发目的或可以安全公开公有 API 的使用案例。
在客户端,通过标头 x-api-key
指定 API 密钥。
例如,如果您的 API_KEY
为 'ABC123'
,则您可以通过 curl
发送 GraphQL 查询,如下所示:
$ curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:ABC123" -d '{ "query": "query { movies { id } }" }' https://YOURAPPSYNCENDPOINT/graphql
AWS_LAMBDA 授权
您可以使用Amazon Lambda函数实现自己的 API 授权逻辑。您可以将 Lambda 函数用于主授权方或次要授权方,但每个 API 可能只有一个 Lambda 授权函数。使用 Lambda 函数进行授权时,以下内容适用:
-
如果 API 启用了
AWS_LAMBDA
和AWS_IAM
授权模式,则 Sigv4 签名不能用作AWS_LAMBDA
授权令牌。 -
如果 API 启用了
AWS_LAMBDA
和OPENID_CONNECT
授权模式或AMAZON_COGNITO_USER_POOLS
授权模式,则 OIDC 令牌不能用作AWS_LAMBDA
授权令牌。请注意,OIDC 代币可以是无记名方案。 -
Lambda 函数为解析器返回的上下文数据不得超过 5MB。
例如,如果您的授权令牌是'ABC123'
,则可以通过 curl 发送 GraphQL 查询,如下所示:
$ curl -XPOST -H "Content-Type:application/graphql" -H "Authorization:ABC123" -d '{ "query": "query { movies { id } }" }' https://YOURAPPSYNCENDPOINT/graphql
在每次查询或变更之前调用 Lambda 函数,但它们的返回值会被缓存:重复的请求只会调用该函数一次,然后才会根据 API ID 和身份验证令牌缓存该函数。默认情况下,此缓存时间为 300 秒(5 分钟),但可以在 API 级别或通过在函数的返回ttlOverride
值中设置值来覆盖该缓存时间。
如果需要,可以指定在调用函数之前验证授权令牌的正则表达式。这些正则表达式用于在调用您的函数之前验证授权令牌的格式是否正确。任何使用与该正则表达式不匹配的令牌的请求都将被自动拒绝。
用于授权的 Lambda 函数需要主体策略appsync.amazonaws.com
才能对其应用Amazon AppSync以允许调用它们。此操作在Amazon AppSync控制台中自动完成;Amazon AppSync控制台不会删除策略。有关向 Lambda 函数附加策略的更多信息,请参阅《Amazon Lambda开发人员指南》中的资源策略。
您指定的 Lambda 函数将接收具有以下形状的事件:
{ "authorizationToken": "ExampleAUTHtoken123123123", "requestContext": { "apiId": "aaaaaa123123123example123", "accountId": "111122223333", "requestId": "f4081827-1111-4444-5555-5cf4695f339f", "queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n", "operationName": "MyQuery", "variables": {} } }
授权函数必须至少返回一个布尔值isAuthorized
,表示请求是否获得授权。 Amazon AppSync识别从 Lambda 授权函数返回的以下密钥:
isAuthorized
(布尔值,必填)-
一个布尔值,表示中的值是否
authorizationToken
被授权调用 GraphQL API。如果此值为真,则 GraphQL API 将继续执行。如果此值为 false,
UnauthorizedException
则引发 deniedFields
(字符串列表,可选)-
即使解析器返回了值
null
,其列表也会被强制更改为。每个项目要么是完全符合条件的字段 ARN,格式为,要
arn:aws:appsync:
么是的简写形式us-east-1
:111122223333
:apis/GraphQLApiId
/types/TypeName
/fields/FieldName
。当两个 API 共享 lambda 函数授权器并且这两个 API 之间的常见类型和字段之间可能存在歧义时,应使用完整的 ARN 表单。TypeName
.FieldName
resolverContext
(JSON 对象,可选)-
一个
$ctx.identity.resolverContext
在解析器模板中可见的 JSON 对象。例如,如果解析器返回以下结构:{ "isAuthorized":true "resolverContext": { "banana":"very yellow", "apple":"very green" } }
解析器模板
$ctx.identity.resolverContext.apple
中的值将为 “very green
”。resolverContext
对象仅支持键值对。不支持嵌套密钥。警告 此 JSON 对象的总大小不得超过 5MB。
ttlOverride
(整数,可选)-
响应应缓存的秒数。如果未返回任何值,则使用 API 中的值(如果已配置)或 300 秒(五分钟)的默认值。如果为 0,则不缓存响应。
Lambda 授权者的超时时间为 10 秒。我们建议设计在尽可能短的时间内执行的函数,以扩展 API 的性能。
多个Amazon AppSync API 可以共享一个身份验证 Lambda 函数。不允许跨账户授权者使用。
在多个 API 之间共享授权功能时,请注意,短格式的字段名称 (
) 可能会无意中隐藏字段。要消除中的字段歧义typename
.fieldname
deniedFields
,可以指定一个明确的字段 ARN,其形式为arn:aws:appsync:
。region
:accountId
:apis/GraphQLApiId
/types/typeName
/fields/fieldName
要将 Lambda 函数添加为默认授权模式,请执行Amazon AppSync以下操作:
以下示例描述了一个 Lambda 函数,该函数演示了 Lambda 函数用作Amazon AppSync授权机制时可能出现的各种身份验证和失败状态:
def handler(event, context): # This is the authorization token passed by the client token = event.get('authorizationToken') # If a lambda authorizer throws an exception, it will be treated as unauthorized. if 'Fail' in token: raise Exception('Purposefully thrown exception in Lambda Authorizer.') if 'Authorized' in token and 'ReturnContext' in token: return { 'isAuthorized': True, 'resolverContext': { 'key': 'value' } } # Authorized with no f if 'Authorized' in token: return { 'isAuthorized': True } # Partial authorization if 'Partial' in token: return { 'isAuthorized': True, 'deniedFields':['user.favoriteColor'] } if 'NeverCache' in token: return { 'isAuthorized': True, 'ttlOverride': 0 } if 'Unauthorized' in token: return { 'isAuthorized': False } # if nothing is returned, then the authorization fails. return {}
规避 siGv4 和 OIDC 代币授权限制
以下方法可用于规避在启用某些授权模式时无法使用您的 Sigv4 签名或 OIDC 令牌作为 Lambda 授权令牌的问题。
如果您想在为的 API 启用AWS_IAM
和授权模式时使用 Sigv4 签名作为 LambdaAWS_LAMBDA
授权令牌,请执行以下操作:Amazon AppSync
-
要创建新的 Lambda 授权令牌,请向 Sigv4 签名添加随机后缀和/或前缀。
-
要检索原始 Sigv4 签名,请通过从 Lambda 授权令牌中移除随机前缀和/或后缀来更新您的 Lambda 函数。然后,使用原始 Sigv4 签名进行身份验证。
如果您想在为的 API 启用授权模式或AMAZON_COGNITO_USER_POOLS
和OPENID_CONNECT
授权模式时使用 OIDC 令牌作为 LambdaAWS_LAMBDA
授权令牌,请执行以下操作:Amazon AppSync
-
要创建新的 Lambda 授权令牌,请向 OIDC 令牌添加随机后缀和/或前缀。Lambda 授权令牌不应包含承载者方案前缀。
-
要检索原始 OIDC 令牌,请通过从 Lambda 授权令牌中移除随机前缀和/或后缀来更新您的 Lambda 函数。然后,使用原始 OIDC 令牌进行身份验证。
AWS_IAM 授权
此授权类型在 GraphQL API 上强制执行签Amazon名版本 4 的签名流程。您可以将 Identity and Access Management (IAM
如果您希望有访问权限的角色执行所有数据操作:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/*" ] } ] }
您可以直接在 AppSync控制YourGraphQLApiId
台的主 API 列表页面上找到 API 的名称下方。或者,您也可以使用 CLI 检索它:aws appsync list-graphql-apis
如果您只想限制对某些 GraphQL 操作的访问,您可以对根 Query
、Mutation
和 Subscription
字段执行此操作。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/<Field-1>", "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/<Field-2>", "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Mutation/fields/<Field-1>", "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Subscription/fields/<Field-1>" ] } ] }
例如,假设您具有以下架构,并且您想要限制能够获取所有文章的访问权限:
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! }
角色的相应 IAM 策略(例如,您可以将其附加到 Amazon Cognito 身份池)如下所示:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/posts" ] } ] }
OPENID_CONNECT 权限
此授权类型强制执行符合 OIDC 的服务提供的 OpenID 连接
发布者 URL 是由您提供给 Amazon AppSync 的唯一需要的配置值,例如 https://auth.example.com
。此 URL 必须可通过 HTTPS 进行寻址。Amazon AppSync 附加/.well-known/openid-configuration
到发行者网址并https://auth.example.com/.well-known/openid-configuration
按照 OpenID Connect Discovery 规范查找 OpenIDjwks_uri
密钥,该密钥通过签名密钥指向 JSON Web 密钥集 (JWKS) 文档。Amazon AppSync 要求 JWKS 包含kty
和的 JSON 字段kid
。
Amazon AppSync 支持多种签名算法。
签名算法 |
---|
RS256 |
RS384 |
RS512 |
PS256 |
PS384 |
PS512 |
HS256 |
HS384 |
HS512 |
ES256 |
ES384 |
ES512 |
建议使用 RSA 算法。提供商颁发的令牌必须包括颁发令牌的时间 (iat
),并可能包含对其进行身份验证的时间 (auth_time
)。您可以在 OpenID Connect 配置中为颁发时间 (iatTTL
) 和身份验证时间 (authTTL
) 提供 TTL 值以进行额外的验证。如果您的提供商授权多个应用程序,还可以提供一个用于按客户端 ID 进行授权的正则表达式 (clientId
)。当您clientId
的 OpenID Connect 配置中存在时,通过要求与令牌中的aud
或azp
声明匹配clientId
来Amazon AppSync 验证声明。
要验证多个客户端 ID,请使用管道运算符 (“|”),即正则表达式中的 “或”。例如,如果您的 OIDC 应用程序有四个客户端分别具有客户端 ID 0A1S2D、1F4G9H、1J6L4B、6GS5MG,要仅对前三个客户端 ID 进行验证,则可以在客户端 ID 字段中放置 1F4G9H|1J6L4B|6GS5MG。
AMAZON_COGNITO_USER_POOLS
此授权类型强制执行 Amazon Cognito 用户池提供的 OIDC 代币。您的应用程序可以利用用户池中的用户和组,并将用户和组与 GraphQL 字段关联以控制访问。
使用 Amazon Cognito 用户池时,您可以创建用户所属的群组。此信息将编码到 JWT 令牌中,当应用程序发送 GraphQL 操作时,会在授权标头中将此令牌发送给 Amazon AppSync 。您可以在架构上使用 GraphQL 指令以控制哪些组可以对字段调用哪些解析程序,从而向客户提供更受控制的访问。
例如,假设您具有以下 GraphQL 架构:
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! } ...
如果您在 Amazon Cognito 用户池中有两个群组(博主和读者),并且您想限制读者以使其无法添加新条目,则您的架构应如下所示:
schema { query: Query mutation: Mutation }
type Query { posts:[Post!]! @aws_auth(cognito_groups: ["Bloggers", "Readers"]) } type Mutation { addPost(id:ID!, title:String!):Post! @aws_auth(cognito_groups: ["Bloggers"]) } ...
请注意,如果您想默认使用特定的访问 grant-or-deny 策略,可以省略该@aws_auth
指令。当您通过控制台或通过以下 CLI 命令创建 GraphQL API 时,可以在用户池配置中指定 grant-or-deny 策略:
$ aws appsync --region us-west-2 create-graphql-api --authentication-type AMAZON_COGNITO_USER_POOLS --name userpoolstest --user-pool-config '{ "userPoolId":"test", "defaultEffect":"ALLOW", "awsRegion":"us-west-2"}'
使用其他授权模式
添加其他授权模式时,可以直接在 Amazon AppSync GraphQL API 级别(即可以在 GraphqlApi
对象上直接配置的 authenticationType
字段),并将其作为架构的默认设置。这意味着任何没有特定指令的类型都必须通过 API 级别授权设置。
在架构级别,您可以使用架构上的指令指定其他授权模式。您可以在架构中的各个字段上指定授权模式。例如,对于 API_KEY
授权,您将在架构对象类型定义/字段上使用 @aws_api_key
。架构字段和对象类型定义支持以下指令:
-
@aws_api_key
- 指定字段是API_KEY
授权的。 -
@aws_iam
- 指定字段是AWS_IAM
授权的。 -
@aws_oidc
- 指定字段是OPENID_CONNECT
授权的。 -
@aws_cognito_user_pools
- 指定字段是AMAZON_COGNITO_USER_POOLS
授权的。 -
@aws_lambda
- 指定字段是AWS_LAMBDA
授权的。
您不能将 @aws_auth
指令与其他授权模式一起使用。@aws_auth
仅适用于 AMAZON_COGNITO_USER_POOLS
授权的上下文,没有其他授权模式。但是,您可以使用 @aws_cognito_user_pools
指令代替 @aws_auth
指令,使用相同的参数。两者之间的主要区别在于您可以在任何字段和对象类型定义上指定 @aws_cognito_user_pools
。
要了解其他授权模式如何工作以及如何在架构上指定它们,我们来看一下以下架构:
schema { query: Query mutation: Mutation } type Query { getPost(id: ID): Post getAllPosts(): [Post] @aws_api_key } type Mutation { addPost( id: ID! author: String! title: String! content: String! url: String! ): Post! } type Post @aws_api_key @aws_iam { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! } ...
对于此架构,假定 AWS_IAM
是 Amazon AppSync GraphQL API 上的默认授权类型。这意味着使用 AWS_IAM
保护没有指令的字段。例如,Query
类型上的 getPost
字段就是这种情况。架构指令使您可以使用多种授权模式。例如,您可以在 Amazon AppSync GraphQL API 上将 API_KEY
配置为其他授权模式,并且可以使用 @aws_api_key
指令标记字段(例如,此示例中的 getAllPosts
)。指令在字段级别工作,因此您也需要授予 API_KEY
访问 Post
类型的权限。您可以通过使用指令标记 Post
类型中的每个字段,或使用 @aws_api_key
指令标记 Post
类型来执行此操作。
要进一步限制对 Post
类型中的字段的访问,可以对 Post
类型中的各个字段使用指令,如下所示。
例如,您可以将 restrictedContent
字段添加到 Post
类型并使用 @aws_iam
指令限制对它的访问。 AWS_IAM
经过身份验证的请求可以访问 restrictedContent
,但是,API_KEY
请求将无法访问它。
type Post @aws_api_key @aws_iam{ id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! restrictedContent: String! @aws_iam } ...
精细访问控制
上述信息演示了如何限制或授权对某些 GraphQL 字段的访问权限。如果您想根据某些条件(例如,根据进行调用的用户以及用户是否拥有数据)来设置对数据的访问控制,则可以使用解析程序中的映射模板。您还可以执行更复杂的业务逻辑,筛选信息中介绍了相关内容。
本节介绍如何使用 DynamoDB 解析器映射模板设置对数据的访问控制。
在继续操作之前,如果您不熟悉中的Amazon AppSync映射模板,则可能需要查看 DynamoDB 的 Resolver 映射模板参考和 Resolver 映射模板参考。
在以下使用 DynamoDB 的示例中,假设您使用的是前面的博客文章架构,并且只有创建帖子的用户才能对其进行编辑。评估过程将是用户在应用程序中获得凭证(例如,使用 Amazon Cognito 用户池),然后将这些凭证作为 GraphQL 操作的一部分进行传递。然后,映射模板将替换条件语句中凭证(如用户名)的值,该值随后将与数据库中的值进行比较。

要添加此功能,请添加 GraphQL 字段 editPost
,如下所示:
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { editPost(id:ID!, title:String, content:String):Post addPost(id:ID!, title:String!):Post! } ...
editPost
的解析程序映射模板(本节末尾的示例中所示)需要针对您的数据存储执行逻辑检查,以仅允许创建文章的用户对文章进行编辑。由于这是一项编辑操作,因此它对应于 DynamoDB 中的 a。UpdateItem
您可以先执行条件检查,然后再执行此操作,同时使用传递的上下文进行用户身份验证。这些信息存储在一个 Identity
对象中,该对象具有以下值:
{ "accountId" : "12321434323", "cognitoIdentityPoolId" : "", "cognitoIdentityId" : "", "sourceIP" : "", "caller" : "ThisistheprincipalARN", "username" : "username", "userArn" : "Sameasabove" }
要在 DynamoDBUpdateItem
调用中使用此对象,您需要在表中存储用户身份信息以供比较。首先,您的 addPost
更改需要存储创建者。其次,您的 editPost
更改需要先执行条件检查,然后才能更新。
下面是 addPost
的请求映射模板的一个示例,此模板将用户身份存储为 Author
列:
{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "postId" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "attributeValues" : { "Author" : $util.dynamodb.toDynamoDBJson($context.identity.username) #foreach( $entry in $context.arguments.entrySet() ) #if( $entry.key != "id" ) ,"${entry.key}" : $util.dynamodb.toDynamoDBJson($entry.value) #end #end }, "condition" : { "expression" : "attribute_not_exists(postId)" } }
请注意,Author
属性通过 Identity
对象填充,该对象来自应用程序。
最后,此处的示例介绍了 editPost
的请求映射模板,此模块仅当请求来自创建文章的用户时,才更新博客文章的内容。
{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id": $util.dynamodb.toDynamoDBJson($ctx.args.id), }, "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args), "condition" : { "expression" : "contains(#author,:expectedOwner)", "expressionNames" : { "#author" : "Author" }, "expressionValues" : { ":expectedOwner" : $util.dynamodb.toDynamoDBJson($context.identity.username) } } }
此示例使用 PutItem
覆盖所有值而不是使用 UpdateItem
,否则在示例中会更加冗长,但同样的概念适用于 condition
语句块。
筛选信息
有时,在成功写入或读取数据源时,您可能无法控制来自数据源的响应,但又不想向客户端发送不必要的信息。在这些情况下,您可以使用响应映射模板筛选信息。
例如,假设您的博客文章 DynamoDB 表上没有相应的索引(例如上的索引Author
)。您可以使用以下映射模板运行 GetItem
查询:
{ "version" : "2017-02-28", "operation" : "GetItem", "key" : { "postId" : $util.dynamodb.toDynamoDBJson($ctx.args.id) } }
这将返回所有的值响应,即使调用方不是创建文章的作者。为了防止发生此类情况,在这种情形下您可以对响应映射模板执行访问检查,如下所示:
{ #if($context.result["Author"] == "$context.identity.username") $utils.toJson($context.result); #end }
如果调用方与此检查不匹配,则只返回 Null 响应。
数据源访问
Amazon AppSync 使用Identity and Access Management (IAM
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "appsync.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
需要将角色的访问策略缩小为仅具有对必要的最少资源集进行操作的权限,这一点非常重要。使用 AppSync 控制台创建数据源和创建角色时,系统会自动为您完成此操作。但是,当使用 IAM 控制台中的内置示例模板在 Amazon AppSync 控制台之外创建角色时,权限不会自动限定在某个资源上,您应该在将应用程序转移到生产之前执行此操作。