注:

使用 Oracle Cloud API 网关创建具有函数验证的安全对象存储下载服务

简介

Oracle Cloud 对象存储是存储大文件或小文件的一种非常便宜和有效的方法。对象存储支持在事件驱动的架构中工作,使得进程执行接近实时。您可以在对象存储中配置事件以触发事件,还可以创建 REST 服务来安全、快速、经济高效地写入或从对象存储读取文件。

如果您有兴趣了解如何实施事件驱动的体系结构,请参阅使用 Oracle Cloud Infrastructure Data Flow 在 Autonomous Database 和 Kafka 中处理大型文件

体系结构

体系结构

Oracle API Gateway 集成

API 网关集成

在本教程中,我们将展示如何配置 Oracle API Gateway 以实施 REST 服务,该功能允许以灵活的方式通过外部身份提供者或 Oracle Cloud Infrastructure (OCI) 本身 (Oracle Identity Cloud Service) 验证 JWT 令牌。无论如何,您都可以验证 JWT 令牌(因为属性可以是文件名的一部分),通过其 SDK 和其他可自定义的流程使用 OCI 服务。

目标

先决条件

任务 1:在 Oracle Identity Cloud Service 中配置应用程序

您可以将任何 REST API 外部身份提供者配置为使用 JWT,例如 Auth0Oracle Identity Cloud Service

在本教程中,我们将使用 Oracle Identity Cloud Service 并调用函数内部的验证。

任务 2:复查代码

这是 API 网关部署中使用的 Python 代码,用于对验证前链接进行授权,以便在对象存储中下载文件。

您可以在此处下载 Python 代码:Python fn Project

python
import io
import json
import logging
import datetime
import jwt
import requests
import base64
import oci

from datetime import timedelta
from fdk import response
from py_zipkin import Encoding #import Zipkin package
from py_zipkin.zipkin import zipkin_span #import Zipkin package
from transport import http_transport #import Zipkin transport

@zipkin_span(service_name='Status: Load File', span_name='statusGetFile')
def auth(ctx, data: io.BytesIO = None):
    auth_token = "invalid"
    token = "invalid"
    apiKey = "invalid"
    expiresAt = (datetime.datetime.utcnow() + timedelta(seconds=60)).replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()

    config = oci.config.from_file("config")
    object_storage = oci.object_storage.ObjectStorageClient(config)
    namespace = object_storage.get_namespace().data

    try:
        auth_token = json.loads(data.getvalue())
        secretID = auth_token.get("secretID")
        clientID = auth_token.get("clientID")

        details = oci.object_storage.models.CreatePreauthenticatedRequestDetails(name="data", access_type="AnyObjectReadWrite", bucket_listing_action="ListObjects", time_expires=expiresAt)

        preauth = object_storage.create_preauthenticated_request(namespace_name=namespace, bucket_name="data", create_preauthenticated_request_details=details)
        preauthstr = str(preauth.data)

        auth = clientID + ":" + secretID
        auth_bytes = auth.encode("ascii")
        auth_base64_bytes = base64.b64encode(auth_bytes)
        auth_base64_message = auth_base64_bytes.decode("ascii")

        headers = {"Authorization": "Basic " + auth_base64_message, "Content-Type": "application/x-www-form-urlencoded"}

        scope = "cihxxxxxxxxxxxxxxxxxowu.apigateway.us-ashburn-1.oci.customer-oci.com/super-scope"
        grant_type = "client_credentials"

        body = {"scope": scope, "grant_type": grant_type}

        url_post = "https://Oracle Identity Cloud Service-4fxxxxxxxxxxxxxxxxxxxxxx9.identity.oraclecloud.com/oauth2/v1/token"
        post_response = requests.post(url_post, headers=headers, data=body)

        jwtTokenDecoded = jwt.decode(post_response.json()['access_token'], options={"verify_signature": False})

        return response.Response(
            ctx,
            status_code=200,
            response_data=json.dumps({"active": True, "principal": "foo", "scope": "bar", "clientId": "1234", "expiresAt": expiresAt, "context": {"username": "wally", "token":
post_response.json()['access_token'], "jwtTokenDecoded": jwtTokenDecoded, "objectID": preauthstr}})
        )

    except (Exception, ValueError) as ex:
        logging.getLogger().info('error parsing json payload: ' + str(ex))
        pass

    return response.Response(
        ctx,
        status_code=401,
        response_data=json.dumps({"active": False, "wwwAuthenticate": "API-key"})
    )

@zipkin_span(service_name='Status: Load File', span_name='statusGetFile')
def handler(ctx, data: io.BytesIO = None):
    with zipkin_span(
            service_name="Status: Load File", #You can change it as you need
            span_name="statusGetFile", #You can change it as you need
            transport_handler=http_transport, #zipkin transport, will use it to upload trace data to OCI APM
            encoding = Encoding.V2_JSON,
            binary_annotations = {"status":"Load File", "objectID":json.loads(data.getvalue()).get("objectID")}, #Custom tag
            sample_rate=100 # this is optional and can be used to set custom sample rates
    ):
        return auth(ctx, data)


我们逐部分分析代码,了解如何:

代码的此部分将状态位置保存在 OCI Observability 中。它使用 Zipkin 框架发布到 OCI APM Observability

python
@zipkin_span(service_name='Status: Load File', span_name='statusGetFile')
def handler(ctx, data: io.BytesIO = None):
    with zipkin_span(
            service_name="Status: Load File", #You can change it as you need
            span_name="statusGetFile", #You can change it as you need
            transport_handler=http_transport, #zipkin transport, will use it to upload trace data to OCI APM
            encoding = Encoding.V2_JSON,
            binary_annotations = {"status":"Load File", "objectID":json.loads(data.getvalue()).get("objectID")}, #Custom tag
            sample_rate=100 # this is optional and can be used to set custom sample rates
    ):

这是代码的 OCI APM 控制台视图,您可以通过如下查询查找文件:

**ServiceName** = 'Status: Load File' and **objectID** = '50 - DR-HA OIC.pdf'

津南省

下一个代码将固定对象存储文件的失效日期和时间。将生成预先验证,并将属性 expiresAt 用于此目标。 timedelta 从当前时间开始添加 60 秒来下载文件。

python
expiresAt = (datetime.datetime.utcnow() + timedelta(seconds=60)).replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()

现在,我们需要基于 OCI CLI 安装中保存的凭证初始化 OCI 对象存储框架。OCI CLI 配置使用 ~/.oci/config 和证书 .pem 文件。因此,您可以在本地安装 OCI CLI 并将用户配置为访问对象存储(请参阅 OCI 文档以在“相关链接”部分中安装 OCI CLI 和对象存储策略),然后将这些 2 个文件复制到此 fn 项目中。

python
config = oci.config.from_file("config")
object_storage = oci.object_storage.ObjectStorageClient(config)
namespace = object_storage.get_namespace().data

下一步将从 Body 参数值获取:secretIDclientIDobjectID

#secretID = the Oracle Identity Cloud Service secretID from the application created to validate the JWT Token
#clientID = the Oracle Identity Cloud Service clientID from the application created to validate the JWT Token
#objectID = the file name in the Object Storage
python
try:
    auth_token = json.loads(data.getvalue())
    secretID = auth_token.get("secretID")
    clientID = auth_token.get("clientID")
    objectID = auth_token.get("objectID")

OCI SDK 可以为许多服务支持对象存储,例如读取和/或写入文件、列出存储桶的内容等。例如,您可以让使用者列出此部分代码中将生成具有预先验证的 URL 的存储桶的所有内容。变量 bucket_name 包含之前创建的对象存储中存储桶的名称,time_expires 表示下载文件的到期日期和时间。

python
details = oci.object_storage.models.CreatePreauthenticatedRequestDetails(name="data", access_type="AnyObjectReadWrite", bucket_listing_action="ListObjects", time_expires=expiresAt)

preauth = object_storage.create_preauthenticated_request(namespace_name=namespace, bucket_name="data", create_preauthenticated_request_details=details)
preauthstr = str(preauth.data)

此部分代码调用 Oracle Identity Cloud Service 来验证 clientIDsecretID 来获取 JWT 标记。JWT 可以解码为 JSON 字符串,在这种情况下,无需签名,但可以使用证书轻松验证签名。

python
auth = clientID + ":" + secretID
auth_bytes = auth.encode("ascii")
auth_base64_bytes = base64.b64encode(auth_bytes)
auth_base64_message = auth_base64_bytes.decode("ascii")

headers = {"Authorization": "Basic " + auth_base64_message, "Content-Type": "application/x-www-form-urlencoded"}

scope = "xxxxxxxxxxxxxxxxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/super-scope"
grant_type = "client_credentials"

body = {"scope": scope, "grant_type": grant_type}

url_post = "https://Oracle Identity Cloud Service-xxxxxxxxxxxxxxxxxxxxxxx.identity.oraclecloud.com/oauth2/v1/token"
post_response = requests.post(url_post, headers=headers, data=body)

jwtTokenDecoded = jwt.decode(post_response.json()['access_token'], options={"verify_signature": False})

这是最后一部分,其中将返回代码为 200 的所有数据。您可以返回应用程序所需的所有信息,并且代码的这一部分将验证验证,从而导致代码 200(已授权/成功)或 401(未授权)。

python
    return response.Response(
        ctx,
        status_code=200,
        response_data=json.dumps({"active": True, "principal": "foo", "scope": "bar", "clientId": "1234", "expiresAt": expiresAt, "context": {"username": "wally", "token": post_response.json()['access_token'], "jwtTokenDecoded": jwtTokenDecoded, "objectID": preauthstr}})
    )

except (Exception, ValueError) as ex:
    logging.getLogger().info('error parsing json payload: ' + str(ex))
    pass

return response.Response(
    ctx,
    status_code=401,
    response_data=json.dumps({"active": False, "wwwAuthenticate": "API-key"})
)



任务 3:配置 OCI API 网关以使用 fn 进行验证

OCI API 网关可以将 API 部署为 OCI 函数。如前所述,代码使用 body ,其中包含由 API 网关配置传递的 clientIDsecretIDobjectID 信息。您可以配置部署:

API 网关 1

API 网关 2

API 网关 3

保存部署后,可以在此处获取 REST 端点。请记住,对象存储 REST API 服务的完整端点是此端点加上末尾的“/”(您之前声明了 / 作为您的路径)。

api 端点

任务 4:测试 API 网关部署

您可以使用 CURLPostman 测试应用程序:

bash
curl --location 'https://xxxxxxxxxxxxxxxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/dummyauthorizer/' \
--header 'Content-Type: text/plain' \
--data '{"clientID": "e3exxxxxxxxxxxxxxxxxc2f", "secretID": "8exxxxxxx-fa5e-xxcf-8xxxc-xxxxxxxxxxx87", "objectID": "any-file.txt"}'

注意:此处使用的属性 objectID 仅用于说明可观察性以及如何在 OCI 控制台的跟踪浏览器上显示状态。

屁股

如果一切正常,您可以看到成功代码 200:

成功结果

将生成预先验证的对象存储的 URL ,您可以在接下来的 60 秒内下载该文件。可以在此处按照以下示例下载文件:

https://objectstorage.us-ashburn-1.oraclecloud.com + [access_uri]

or

https://objectstorage.us-ashburn-1.oraclecloud.com/p/eL5C0R0luN_cTNn-vUF7_Dx_z2N4w7IXemKr5y61cSRxZZPRXcR2Yj1dNCaJBDK8/n/idavixsf5sbx/b/data/o/calico.yaml

您可以使用此命令行下载、上载或查看存储桶文件:

- TO UPLOAD A FILE: `curl https://objectstorage.us-ashburn-1.oraclecloud.com/p/HoPudIF45Bj6J5-Qy3J1D9dOplLuKtECRFhvOTkKAtBjJXkOTDx0Pt8gXbOOEoRx/n/idavixsf5sbx/b/data/o/ --upload-file func.py`

- TO DOWNLOAD A FILE: `curl https://objectstorage.us-ashburn-1.oraclecloud.com/p/3ZyXd6PchrTFrp1oxmedamSG1ojwQa3BxPUyonAA-q1mf3QAe5STpDrt89eYITPf/n/idavixsf5sbx/b/data/o/func.py`

- TO LIST BUCKET: `curl https://objectstorage.us-ashburn-1.oraclecloud.com/p/ODVRMB71kD0SHWuoY4ojVd93nmIiy8u0zrxA56T7FBaohAgA7k8KOLAIlhxjcveE/n/idavixsf5sbx/b/data/o/``

任务 5:配置可观察性仪表盘

基本上,每个 OCI 资源都可以在仪表盘中显示指标,并且这些资源的许多事件都会触发一个操作。在此演示中,您可以配置仪表盘来显示已在对象存储上写入或读取的文件数:

监测能力

您可以按如下方式配置仪表盘查询:

确认

更多学习资源

探索 docs.oracle.com/learn 上的其他实验室,或者访问 Oracle Learning YouTube 频道上的更多免费学习内容。此外,请访问 education.oracle.com/learning-explorer 成为 Oracle Learning Explorer。

有关产品文档,请访问 Oracle 帮助中心