相关文章推荐

1. 简介

本文介绍了如何通过Keycloak,并结合AWS API Gateway内置的授权功能,完成对AWS资源请求的鉴权过程。API Gateway帮助开发者安全的的创建、发布、维护并管理API的访问。在中国区,由于Cognito仍未上线,因此使用Keycloak作为API调用的鉴权服务,具有重要的实际意义。

本文共分为四大模块:

简介 :对Keycloak、OAuth2.0以及AWS 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通过AWS Gateway的Authorizer完成请求的鉴权。

1.2 关于OAuth2.0

OAuth2.0全称为Open Authorization 2.0,为用于鉴权的协议。通过OAuth协议,可以授权第三方应用请求用户的资源,而不需要资源的拥有者直接向第三方提供任何验证凭据信息。
OAuth2.0 鉴权流程

1.3 关于AWS API Gateway Authorizer

在本文中,我们以HTTP API为例,利用HTTP API已内置的授权功能进行API请求的鉴权。如果使用REST API,则需要通过结合Lambda进行JWT Token的校验,可参照此 说明文档 进行配置。

2. 配置说明

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

  • GET /items:不需要进行鉴权,可以直接通过API Gateway获取DynamoDB数据。
  • POST /items:需要进行鉴权,通过API Gateway校验请求Token,验证成功后,向DynamoDB写入数据。
  • 2.2 预置条件

  • 创建一个DynamoDB Table,DemoTable。
  • 在AWS API Gateway创建HTTP API,并与Lambda进行集成。
  • DynomoDB 设置

  • 分区键 :pk
  • 排序键 :sk
  • 其他保留默认配置
  • API Gateway 设置

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

    Lambda 函数名 Runtime Permission GET /items GetItemsLambda Python 3.9 GetItemsLambdaRole DDB_TABLE demoDDB POST /items CreateItemsLambda Python 3.9 CreateItemsLambdaRole DDB_TABLE demoDDB 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):
            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建立AWS SSO登陆Console

    2.3.2 Keycloak realm的建立 

    Realm 可以理解为域,用于管理用户、用户凭据、角色和用户组。通常我们需要在realm里创建client,不同的应用客户端应在realm中配置不同的client。当进行鉴权时,请求资源的应用客户端会向鉴权服务器请求Auth Code,正如上文中OAuth2.0鉴权流程 第2步所示。为了完成Keycloak realm的建立,我们可以:

  • 登陆keycloak admin console,点击 “Administration Console”并登陆;
  • 创建一个realm,并进入到realm中;
  • 2.3.3 Keycloak Client的建立

  • 在realm中点击“Configure-> Clients-> Create”;
  • 按照下面的说明输入相关信息;注意选择Standard Flow Enabled,这将会使client的鉴权按照0的“Authorization Code Flow”完成,即OAuth2.0鉴权流程 所示。
  • 注意,如没有特别的需求,尽量避免使用“Implicit Flow”即关闭“Implicit Flow Enabled”。这种方式没有授权码这个中间步骤,所以称为(授权码)”隐藏式”(implicit)。这将会把Token直接传给前端,是很不安全的,因此,只能用于一些安全要求不高的场景。

  • 在“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的建立

  • 创建测试用的User,点击左侧“Manage-> Users-> Add user”;
  • 输入User信息;
  • 设定密码,输入密码,并输入Password Confirmation,点击Reset Password以完成设置。
  • 2.4 API Gateway Authorizer的设定

  • 进入到上文创建的API Gateway中,由于我们的目的是在创建item时,需要进行API鉴权,因此只在POST /items的路由上附加授权方即可:
  • 输入相关信息;
  • 身份来源:通常情况下,在请求资源服务器时,会将JWT Token写入到请求头中的Authorization字段,因此可以保留默认。
  • 发布者URL:针对Keycloak为https://{Keycloak_URL}/auth/realms/{realm}/
  • 受众:关联的受众,此处输入account
  • 3. 验证JWT Authorizer

    3.1 请求Auth Code

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

  • 接下来,我们通过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。
  • 将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 请求创建资源

  • 通过Postman模仿应用客户端,模拟创建item的过程;
  • Authorization:将2中返回的access_token 粘贴在Authorization header中,格式为:Bearer {access_token}
  • 将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"
  • 通过浏览器GET /item 查看item是否写入成功:
  • 4. 总结

    由于当前Cognito在中国区仍不可用,Keycloak可以作为一个替代方案,完成用户的单点登录和API的鉴权。本blog提供了如何结合API Gateway 的HTTP API,利用Keycloak和JWT进行API鉴权的演示。通过这种方式,应用客户端在请求AWS资源时,需要通过Keycloak服务器进行校验,并换取有效的JWT Token,以获得访问资源的权限。

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

    OAuth2.0 Grant Type

    Json Web Token

    使用 SAML 和 Keycloak 建立 AWS SSO 登录 Console

    How to secure API Gateway HTTP endpoints with JWT authorizer

     
    推荐文章