关于amazon:通过-Keycloak-结合-OAuth20协议进行-Amazon-API-Gateway-鉴权

6次阅读

共计 7345 个字符,预计需要花费 19 分钟才能阅读完成。

1. 简介

本文介绍了如何通过 Keycloak,并联合 Amazon API Gateway 内置的受权性能,实现对 Amazon 资源申请的鉴权过程。API Gateway 帮忙开发者平安的的创立、公布、保护并治理 API 的拜访。在中国区,因为 Cognito 仍未上线,因而应用 Keycloak 作为 API 调用的鉴权服务,具备重要的实际意义。

亚马逊云科技开发者社区为开发者们提供寰球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、流动与比赛等。帮忙中国开发者对接世界最前沿技术,观点,和我的项目,并将中国优良开发者或技术举荐给寰球云社区。如果你还没有关注 / 珍藏,看到这里请肯定不要匆匆划过,点这里让它成为你的技术宝库!

本文共分为四大模块:

简介:对 Keycloak、OAuth2.0 以及 Amazon API Gateway Authorizer 进行了介绍;

配置阐明:对环境的设定进行了具体的阐明,包含 DynamoDB 的设定以及 Keycloak 环境的搭建和设置;

验证 JWT Authorizer:通过 Postman 对 API Gateway 的受权性能进行验证;

总结:对全文的总结。

1.1 对于 Keycloak

Keycloak 是一个开源并广泛应用于用户身份治理与受权的解决方案。Keycloak 反对多种协定和规范,包含 OpenID Connect,OAuth2.0 和 SAML2.0。同时 Keycloak 能够集成与已有的 LDAP 或者 Active Directory 服务集成,用于单点登录。基于 OAuth2.0,Keycloak 还能够通过 JWT Token 实现对 API 的鉴权,本文基于此场景,联合 Keycloak 通过 Amazon Gateway 的 Authorizer 实现申请的鉴权。

1.2 对于 OAuth2.0

OAuth2.0 全称为 Open Authorization 2.0,为用于鉴权的协定。通过 OAuth 协定,能够受权第三方利用申请用户的资源,而不须要资源的拥有者间接向第三方提供任何验证凭据信息。OAuth2.0 鉴权流程

1.3 对于 Amazon API Gateway Authorizer

在本文中,咱们以 HTTP API 为例,利用 HTTP API 已内置的受权性能进行 API 申请的鉴权。如果应用 REST API,则须要通过联合 Lambda 进行 JWT Token 的校验,可参照此阐明文档进行配置。

2. 配置阐明

在本设计中,用户通过调用 API Gateway 中定义好的 API,拜访 Amazon 上的数据库资源。咱们通过定义 2 个路由,并集成 Lambda 函数,实现对 DynamoDB 数据的读 / 写。

  • GET /items:不须要进行鉴权,能够间接通过 API Gateway 获取 DynamoDB 数据。
  • POST /items:须要进行鉴权,通过 API Gateway 校验申请 Token,验证胜利后,向 DynamoDB 写入数据。

    2.2 预置条件

  • 创立一个 DynamoDB Table,DemoTable。
  • 在 Amazon API Gateway 创立 HTTP API,并与 Lambda 进行集成。
    DynomoDB 设置
  • 分区键 :pk
  • 排序键 :sk
  • 其余保留默认配置
    API Gateway 设置

创立 API Gateway,为 API Gateway 创立 2 条路由,并关联 Lambd 函数,列表如下:

Lambda 函数样例

  • GetItemsLambda
import boto3
import os
import json
import botocore
 
def lambda_handler(event, context):
    try:
        client = boto3.resource("dynamodb")
        table_name = os.environ.get('DDB_TABLE')
        table = client.Table(table_name)
        scanning_result = table.scan()
 
        return {
            'statusCode': 200,
            'body': json.dumps(scanning_result),
            'headers': {'Content-Type': 'application/json'},
            'isBase64Encoded': 'false'
        }
    except botocore.exceptions.ClientError as e:
        print(e)
  • GetItemsLambdaRole Policy:
"Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "dynamodb:DescribeTable",
                "dynamodb:Query",
                "dynamodb:Scan"
            ],
            "Resource": ["arn:aws:dynamodb:us-east-1:123456789012:table/DemoTable"],
            "Effect": "Allow"
        }
    ]
}
  • CreateItemsLambda:
import json
import datetime
import boto3
import os
import botocore
import uuid
 
def lambda_handler(event, context):
    try:
        client = boto3.client('dynamodb')
        table_name = os.environ.get('DDB_TABLE')
        req_body = json.loads(event["body"])
        req_user = event["requestContext"]["authorizer"]["jwt"]["claims"]["email"]
        ct = datetime.datetime.now()
        client.put_item(
            TableName = table_name,
            Item = {'pk': {'S':req_user},
                'sk': {'S':'item#' + str(uuid.uuid4())},
                'title': {'S':req_body["title"]},
                'timeCreated': {'S': ct.isoformat()}
                 
            }
                )
                 
        return {
            'statusCode': 200,
            'body': json.dumps('New Item Created')
        }
    except botocore.exceptions.ClientError as e:
        print(e)
  • CreateItemsLambdaRole Policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "dynamodb:BatchGetItem",
                "dynamodb:GetRecords",
                "dynamodb:GetShardIterator",
                "dynamodb:Query",
                "dynamodb:GetItem",
                "dynamodb:Scan",
                "dynamodb:ConditionCheckItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem"
            ],
            "Resource": ["arn:aws:dynamodb:us-east-1:123456789012:table/DemoTable"],
            "Effect": "Allow"
        }
    ]
}

2.3 Keycloak 的设定

2.3.1 Keycloak 的装置

对于 Keycloak 的装置,请参照 blog:应用 SAML 和 Keycloak 建设 Amazon SSO 登陆 Console

2.3.2 Keycloak realm 的建设

Realm 能够了解为域,用于治理用户、用户凭据、角色和用户组。通常咱们须要在 realm 里创立 client,不同的利用客户端应在 realm 中配置不同的 client。当进行鉴权时,申请资源的利用客户端会向鉴权服务器申请 Auth Code,正如上文中 OAuth2.0 鉴权流程 第 2 步所示。为了实现 Keycloak realm 的建设,咱们能够:

  1. 登陆 keycloak admin console,点击“Administration Console”并登陆;
  1. 创立一个 realm,并进入到 realm 中;

2.3.3 Keycloak Client 的建设

  1. 在 realm 中点击“Configure-> Clients-> Create”;
  1. 依照上面的阐明输出相干信息;留神抉择 Standard Flow Enabled,这将会使 client 的鉴权依照 0 的“Authorization Code Flow”实现,即 OAuth2.0 鉴权流程所示。

留神,如没有特地的需要,尽量避免应用“Implicit Flow”即敞开“Implicit Flow Enabled”。这种形式没有受权码这个两头步骤,所以称为(受权码)”隐藏式”(implicit)。这将会把 Token 间接传给前端,是很不平安的,因而,只能用于一些平安要求不高的场景。

  1. 在“Credentials tab”中,抉择 Client Id and Secret,此时会生成一串随机字符串作为 Secret。在上文中 OAuth2.0 鉴权流程 第 6 步中替换 Token 的过程中,Secret 将作为 client_secret 的值,在利用客户端的 POST Body 中被发送到 Keycloak Client 中,以确保向 Client 替换的 Token 颁布给了正确的利用客户端。

2.3.4 Keycloak User 的建设

  1. 创立测试用的 User,点击左侧 Manage-> Users-> Add user”;
  1. 输出 User 信息;
  1. 设定明码,输出明码,并输出 Password Confirmation,点击 Reset Password 以实现设置。

2.4 API Gateway Authorizer 的设定

  1. 进入到上文创立的 API Gateway 中,因为咱们的目标是在创立 item 时,须要进行 API 鉴权,因而只在 POST /items 的路由上附加受权方即可:
  1. 输出相干信息;
  • 身份起源 :通常状况下,在申请资源服务器时,会将 JWT Token 写入到申请头中的 Authorization 字段,因而能够保留默认。
  • 发布者 URL:针对 Keycloak 为 https://{Keycloak_URL}/auth/realms/{realm}/
  • 受众 :关联的受众,此处输出 account

3. 验证 JWT Authorizer

3.1 申请 Auth Code

  1. 依据 OAuth2.0 鉴权流程,当用户申请资源时,利用客户端将会向鉴权服务器发送 GET 申请,以申请 Auth Code;
GET https://{Keycloak_URL}/auth/realms/Keycloaksso/protocol/openid-connect/auth?response_type=code&client_id=Keycloak
  1. 在此过程中,鉴权服务器将会返回 Keycloak 的登陆界面,要求用户输出其用户名明码,在此处,咱们输出 Keycloak User 的建设章节中创立的 User 的用户名和明码。
  1. 输出正确的用户名和明码后,Keycloak 将会通过 Query String 返回 Auth Code,如下图所示;

3.2 替换 JWT Token

  1. 接下来,咱们通过 Postman 模拟利用客户端,模仿通过 POST Auth Code 换取 JWT Token 的过程;
  • client_id: Keycloak 的 client ID,为 Keycloak Client 的建设 章节中创立;
  • grant_type: 0 的鉴权模式,咱们通过 authorization code 模式鉴权,这也是最常见的模式;
  • client_secret: 用于 Client 利用客户端的验证;
  • code:Auth Code,由鉴权服务器返回,用于替换 Token。
  1. 将 Postman 的申请用 curl 实现:
curl --location --request POST 'https://{Keycloak_URL}/auth/realms/Keycloaksso/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: AUTH_SESSION_ID=a0b56dbf-19b0-4d16-b254-c25248834c01.Keycloak-5b7448f8cf-v5wg6; AUTH_SESSION_ID_LEGACY=a0b56dbf-19b0-4d16-b254-c25248834c01.Keycloak-5b7448f8cf-v5wg6; KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyYjQ0M2Q2ZS00MzNiLTQwYTQtYjdlMi03MDk2Mjg1YTJkYmMifQ.eyJjaWQiOiJrZXljbG9hayIsInB0eSI6Im9wZW5pZC1jb25uZWN0IiwicnVyaSI6IioiLCJhY3QiOiJBVVRIRU5USUNBVEUiLCJub3RlcyI6eyJpc3MiOiJodHRwczovL2F1dGguY2lhdGVzdC50b3AvYXV0aC9yZWFsbXMva2V5Y2xvYWtzc28iLCJyZXNwb25zZV90eXBlIjoiY29kZSJ9fQ.5T6tBz-j7vbfzvhHBpPnQ2ebRqYC69gNF-EMlWmsA8Q' \
--data-urlencode 'client_id=Keycloak' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_secret=bba58d29-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
--data-urlencode 'code=74926370-xxxx-xxxx-xxxx-xxxxxxxxxxxx.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Response:

    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldU….eyJleHAiOjE2NTYyNDExOTksImlhdCI6MT….m91pmRMmSnA0D37qF4_...",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldU….eyJleHAiOjE2NTYyNDI2OTksImlhdCI6MT….OaDarszhAnyd3NKZTiZ…",
    "token_type": "Bearer",
    "not-before-policy": 0,
    "session_state": "aa8b66e0-xxxx-xxxx-xxxx-6ff28b1213d5",
    "scope": "profile email"
}

3.3 向 API Gateway 申请创立资源

  1. 通过 Postman 模拟利用客户端,模仿创立 item 的过程;
  • Authorization:将 2 中返回的 access_token 粘贴在 Authorization header 中,格局为:Bearer {access_token}
  1. 将 Postman 的申请用 curl 实现:
curl --location --request POST 'https://80iiueir8b.execute-api.us-east-1.amazonaws.com/items' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldU….eyJleHAiOjE2NTYyNDExOTksImlhdCI6MT….m91pmRMmSnA0D37qF4_...\
--header 'Content-Type: application/json' \
--data-raw '{"title":"nike high heel"}'

Response:

"New Item Created"

  1. 通过浏览器 GET /item 查看 item 是否写入胜利:

4. 总结

因为以后 Cognito 在中国区仍不可用,Keycloak 能够作为一个代替计划,实现用户的单点登录和 API 的鉴权。本 blog 提供了如何联合 API Gateway 的 HTTP API,利用 Keycloak 和 JWT 进行 API 鉴权的演示。通过这种形式,利用客户端在申请 Amazon 资源时,须要通过 Keycloak 服务器进行校验,并换取无效的 JWT Token,以取得拜访资源的权限。

如有趣味理解本文提到的更多技术,请参照:

OAuth2.0 Grant Type

Json Web Token

应用 SAML 和 Keycloak 建设 Amazon SSO 登录 Console

How to secure API Gateway HTTP endpoints with JWT authorizer

本篇作者

李潇翌  亚马逊云科技业余服务团队平安参谋,负责云平安合规、云平安解决方案等的征询设计及落地施行,致力于为客户上云提供平安最佳实际,并解决客户上云中碰到的平安需要。

文章起源:https://dev.amazoncloud.cn/column/article/630b4bf9269604139cb…

正文完
 0