本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
配置授权和身份验证以保护您的 GraphQL APIs
AWS AppSync 为保护 GraphQL 提供了以下授权类型APIs:API密钥、Lambda、、Op IAM enID Connect 和 Cognito 用户池。每个选项都提供不同的安全方法:
-
API密钥授权:控制未经身份验证的限制APIs,提供一个简单的安全选项。
-
Lambda 授权:启用自定义授权逻辑,详细解释函数输入和输出。
-
IAM授权:利用签 AWS名版本 4 签名流程,允许通过策略进行精细的访问控制。IAM
-
OpenID Connect 授权:与OIDC兼容的服务集成,用于用户身份验证。
-
Cognito 用户池:使用 Cognito 的用户管理功能实现基于群组的访问控制。
授权类型
您可以通过五种方式授权应用程序与您的 AWS AppSync Graph API QL 进行交互。您可以通过在 AWS AppSync API或CLI调用中指定以下授权类型值之一来指定所使用的授权类型:
-
-
API_KEY
-
用于使用API密钥。
-
-
-
AWS_LAMBDA
-
用于使用 AWS Lambda 函数。
-
-
-
AWS_IAM
-
用于使用 AWS Identity and Access Management (IAM
) 权限。
-
-
-
OPENID_CONNECT
-
适用于使用 OpenID Connect 提供程序。
-
-
-
AMAZON_COGNITO_USER_POOLS
-
适用于使用 Amazon Cognito 用户池。
-
这些基本授权类型适用于大多数开发人员。对于更高级的用例,您可以通过控制台CLI、和添加其他授权模式 AWS CloudFormation。对于其他授权模式, AWS 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授权
未经身份验证APIs需要比经过身份验证更严格的限制。APIs控制未经身份验证的 GraphQL 端点的限制的一种方法是使用密钥。APIAPI密钥是应用程序中的硬编码值,在您创建未经身份验证的 GraphQL 端点时由 AWS AppSync 服务生成。您可以从控制台CLI、或AWS AppSync API参考资料中轮换API密钥。
API密钥最多可配置 365 天,您可以将现有到期日期从该日起最多再延长 365 天。API建议将密钥用于开发目的或可以安全地向公众公开的用例API。
在客户端上,API密钥由标题指定x-api-key
。
例如,如果您的 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授权
您可以使用 AWS Lambda 函数实现自己的API授权逻辑。您可以将 Lambda 函数用于您的主授权方或辅助授权方,但每个授权方可能只有一个 Lambda 授权函数。API在使用 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 和身份验证令牌缓存返回值。默认情况下,缓存未开启,但可以在API级别上启用缓存,也可以通过在函数的返回ttlOverride
值中设置值来启用缓存。
如果需要,可以指定一个正则表达式,以在调用函数之前验证授权令牌。这些正则表达式用于在调用函数之前验证授权令牌格式是否正确。将自动拒绝使用的令牌与该正则表达式不匹配的任何请求。
用于授权的 Lambda 函数需要对其应用委托人策略 AWS AppSync 才能允许调用它们。appsync.amazonaws.com
此操作在 AWS AppSync 控制台中自动完成; AWS AppSync 控制台不会删除该策略。有关向 Lambda 函数附加策略的更多信息,请参阅开发人员指南中的基于资源的策略。 AWS 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": {} } "requestHeaders": {
application request headers
} }
该event
对象包含应用程序客户端在请求中发送到的标头 AWS AppSync。
授权函数必须至少返回一个布尔值isAuthorized
,表示请求是否获得授权。 AWS AppSync 识别从 Lambda 授权函数返回的以下密钥:
注意
operationName
中用于 WebSocket 连接操作requestContext
的值设置 AWS AppSync 为 “DeepDish:Connect
”。
isAuthorized
(布尔值,必需)-
一个布尔值,表示中的值
authorizationToken
是否有权调用 GraphQL API。如果此值为真,则继续执行 GraphQL API。如果该值为 false,则会引发
UnauthorizedException
。 deniedFields
(字符串列表,可选)-
强制更改为
null
的值列表,即使从解析器中返回值也是如此。每个项目要么是形式的完全限定字段ARN,要
arn:aws:appsync:
么是简写形式us-east-1
:111122223333
:apis/GraphQLApiId
/types/TypeName
/fields/FieldName
。当两者APIs共享 Lambda 函数授权者并且两者之间的常见类型和字段之间可能存在歧义时,应使用完整ARN表单。APIsTypeName
.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则使用中的值。如果为 0,则不会缓存响应。
Lambda 授权者的超时为 10 秒。我们建议您设计在尽可能短的时间内执行的函数,以扩展您的性能API。
多个 AWS AppSync APIs可以共享一个身份验证 Lambda 函数。不允许跨账户授权者使用。
在多个字段之间共享授权功能时APIs,请注意,短格式的字段名称 (
) 可能会无意中隐藏字段。要消除中某个字段的歧义typename
.fieldname
deniedFields
,您可以按以下形式指定一个明确ARN的字段。arn:aws:appsync:
region
:accountId
:apis/GraphQLApiId
/types/typeName
/fields/fieldName
要在 AWS AppSync中添加 Lambda 函数以作为默认授权模式,请执行以下操作:
以下示例介绍了一个 Lambda 函数,以说明 Lambda 函数在作为 AWS 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 授权令牌的问题。
如果要在启用AWS_IAM
和授权模式时使用 Sigv4 签名作为 Lambda AWS_LAMBDA
授权令牌API,请执行以下 AWS AppSync操作:
-
要创建新的 Lambda 授权令牌,请在 SigV4 签名中添加随机后缀和/或前缀。
-
要检索原始 SigV4 签名,请从 Lambda 授权令牌中删除随机前缀和/或后缀以更新您的 Lambda 函数。然后,使用原始 SigV4 签名进行身份验证。
如果要在OIDC启用授权模式或AMAZON_COGNITO_USER_POOLS
和授权模式时将该OPENID_CONNECT
令牌用作 Lambda AWS_LAMBDA
授权令牌API,请执行以下操作: AWS AppSync
-
要创建新的 Lambda 授权令牌,请为令牌添加随机后缀和/或前缀。OIDCLambda 授权令牌不应包含持有者方案前缀。
-
要检索原始OIDC令牌,请从 Lambda 授权令牌中移除随机前缀和/或后缀,从而更新您的 Lambda 函数。然后,使用原始令OIDC牌进行身份验证。
AWS_IAM授权
此授权类型在 GraphQL API 上强制执行AWS 签名版本 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 授权
此授权类型强制使用由兼容的服务提供的 OpenID
颁发者URL是您向其提供的唯一必需配置值 AWS AppSync (例如,https://auth.example.com
)。这URL必须是可以解决的。HTTPS AWS AppSync 附加/.well-known/openid-configuration
到颁发者URL并按照 OpenID Connect Discovery 规范找到 https://auth.example.com/.well-known/openid-configuration
OpenID 配置。jwks_uri
密钥,该密钥指向带有签名密钥的 JSON Web 密钥集 (JWKS) 文档。 AWS AppSync 要求包含JWKSkty
和的JSON字段kid
。
AWS 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
来 AWS AppSync 验证声明。
要验证多个客户端,IDs请使用管道运算符 (“|”),这是正则表达式中的 “或”。例如,如果您的OIDC应用程序有四个客户端,其客户端(IDs例如 0A1S2D、1F4G9H、1J6L4B、6)仅验证前三个客户端GS5MG,则应在客户端 IDs ID 字段中放置 1F4G9H|1J6L4B| GS5MG 6。
AMAZON_COGNITO_USER_POOLS 授权
此授权类型强制使用 Amazon Cognito 用户池提供的OIDC令牌。您的应用程序可以利用您的用户池和其他 AWS 账户的用户池中的用户和群组,并将它们与 GraphQL 字段关联以控制访问权限。
在使用 Amazon Cognito 用户池时,您可以创建用户所属的组。这些信息以JWT令牌编码,您的应用程序在发送 GraphQL AWS AppSync 操作时将其发送到授权标头中。您可以在架构上使用 GraphQL 指令以控制哪些组可以对字段调用哪些解析器,从而向客户提供更受控制的访问。
例如,假设您具有以下 GraphQL 架构:
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! } ...
如果您在 Amazon Cognito 用户池中具有两个组(bloggers 和 readers),并希望限制读者以使他们无法添加新条目,则架构应如下所示:
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
指令。API通过控制台或以下CLI命令创建 GraphQL 时,可以在用户池配置中指定 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"}'
使用其他授权模式
添加其他授权模式时,可以直接在 AWS 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
是 AWS AppSync Graph API QL 上的默认授权类型。这意味着使用 AWS_IAM
保护没有指令的字段。例如,Query
类型上的 getPost
字段就是这种情况。架构指令使您可以使用多种授权模式。例如,您可以在 AWS AppSync GraphQL 上API_KEY
配置为其他授权模式API,也可以使用@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 解析器映射模板设置数据的访问控制。
在继续操作之前,如果您不熟悉中的 AWS AppSync映射模板,则可能需要查看 DynamoDB 的解析器映射模板参考和 DynamoDB 的解析器映射模板参考。
在使用 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 中的 UpdateItem
。您可以先执行条件检查,然后再执行此操作,同时使用传递的上下文进行用户身份验证。这些信息存储在一个 Identity
对象中,该对象具有以下值:
{ "accountId" : "12321434323", "cognitoIdentityPoolId" : "", "cognitoIdentityId" : "", "sourceIP" : "", "caller" : "ThisistheprincipalARN", "username" : "username", "userArn" : "Sameasabove" }
要在 DynamoDB UpdateItem
调用中使用该对象,您需要在表中存储用户身份信息以进行比较。首先,您的 addPost
变更需要存储创建者。其次,您的 editPost
变更需要先执行条件检查,然后才能更新。
下面是 addPost
的解析器代码示例,用于将用户身份存储为 Author
列:
import { util, Context } from '@aws-appsync/utils'; import { put } from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const { id: postId, ...item } = ctx.args; return put({ key: { postId }, item: { ...item, Author: ctx.identity.username }, condition: { postId: { attributeExists: false } }, }); } export const response = (ctx) => ctx.result;
请注意,Author
属性通过 Identity
对象填充,该对象来自应用程序。
最后,此处的示例介绍了 editPost
的解析器代码,此代码仅当请求来自创建博客文章的用户时,才更新该博客文章的内容。
import { util, Context } from '@aws-appsync/utils'; import { put } from '@aws-appsync/utils/dynamodb'; export function request(ctx) { const { id, ...item } = ctx.args; return put({ key: { id }, item, condition: { author: { contains: ctx.identity.username } }, }); } export const response = (ctx) => ctx.result;
此示例使用 PutItem
覆盖所有值,而不是使用 UpdateItem
,但同样的概念适用于 condition
语句块。
筛选信息
有时,在成功写入或读取数据来源时,您可能无法控制来自数据来源的响应,但又不想向客户端发送不必要的信息。在这些情况下,您可以使用响应映射模板筛选信息。
例如,假设您的博客文章 DynamoDB 表上没有相应的索引(例如 Author
上的索引),您可以使用以下解析器:
import { util, Context } from '@aws-appsync/utils'; import { get } from '@aws-appsync/utils/dynamodb'; export function request(ctx) { return get({ key: { ctx.args.id } }); } export function response(ctx) { if (ctx.result.author === ctx.identity.username) { return ctx.result; } return null; }
即使调用方不是创建博客文章的作者,请求处理程序也会获取该项目。为了防止它返回所有数据,响应处理程序会进行检查,以确保调用方与项目的作者匹配。如果调用方与此检查不匹配,则只返回 Null 响应。
数据来源访问
AWS AppSync 使用 Identity and Access Management (IAM
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "appsync.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
需要将角色的访问策略缩小为仅具有对必要的最少资源集进行操作的权限,这一点非常重要。使用 AppSync 控制台创建数据源和创建角色时,系统会自动为您完成此操作。但是,当使用控制台中的内置示例模板在IAM控制台之外创建角色时,权限不会自动限制在资源上,您应该在将应用程序移至生产环境之前执行此操作。 AWS AppSync