你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用 Azure API 管理为 GeoCatalog 创建 API 代理

本文将指导你如何在 Microsoft Planetary Computer Pro GeoCatalog 前,将 Azure API 管理(APIM)设置为一个 API 代理。 使用此配置,可以:

  • 启用匿名访问:调用方不需要自己的Microsoft Entra 凭据。 APIM 使用托管标识代表其向 GeoCatalog 进行身份验证。
  • 非 Entra 身份验证:调用方可以支持基于非 Entra 的身份验证方法。 APIM 使用托管标识代表其向 GeoCatalog 进行身份验证。
  • 强制实施集合级访问控制:限制哪些 Spatiotemporal Access Catalog (STAC) 集合通过代理可见,即使 GeoCatalogs 本身不支持集合级基于角色的访问控制(RBAC)。

下图演示了添加 APIM 代理前后的体系结构:

之前 每个调用方都直接向 GeoCatalog 进行身份验证:

caller ──(Entra token)──► GeoCatalog

APIM 位于调用方和 GeoCatalog 之间,处理身份验证和访问控制:

caller ──(anonymous / APIM Subscription Keys)──► APIM ──(managed identity token)──► GeoCatalog

先决条件

将托管标识分配到 APIM

在 APIM 向 GeoCatalog 进行身份验证之前,需要将用户分配的托管标识与 APIM 实例相关联。

  1. Azure 门户中,导航到 API 管理实例。
  2. 从左侧栏中选择“身份”
  3. 选择“用户分配”选项卡。
  4. 选择“添加”,然后选择在 GeoCatalog 上具有 GeoCatalog 读者角色的用户分配的托管标识。
  5. 选择“添加”以确认。

在 APIM 中创建 API

在 APIM 中定义一个新的 API,该 APIM 将请求代理到 GeoCatalog 后端。

  1. 在 APIM 实例中,从左侧栏中选择 API

  2. 选择 “+ 添加 API>HTTP”。

  3. 使用以下设置配置 API:

    设置 价值
    显示名称 描述性名称(例如) GeoCatalog API
    Web 服务 URL GeoCatalog 终结点(例如 https://<name>.<id>.<region>.geocatalog.spatio.azure.com
    URL 架构 HTTPS
    API URL 后缀 留空(根路径)
    需要订阅 ,用于匿名访问; ,对于基于订阅密钥的访问
  4. 选择“创建”

定义 API 操作

添加以下操作以匹配 GeoCatalog API 图面。 通配符 (/*) 操作将所有匹配的请求转发到后端。 显式集合操作使你以后能够应用特定于集合的策略进行访问控制。

显示名称 方法 URL 模板
GET GET /*
获取集合项 GET /stac/collections/{collection_id}/items
获取单个集合 GET /stac/collections/{collection_id}
获取集合子资源 GET /stac/collections/{collection_id}/*
POST POST /*

若要添加每个操作,请执行以下操作:

  1. 选择创建的 API。
  2. 选择 “+ 添加操作”
  3. 输入上表中的 显示名称方法和URL 模板
  4. 选择“保存”

配置 API 级策略

API 级策略处理整个 API 的身份验证和 URL 重写。 此策略从用户分配的托管标识获取令牌,并将其附加到转发到 GeoCatalog 后端的每个请求。

  1. 选择创建的 API,然后选择 “所有操作”。
  2. “入站处理 ”部分中,选择 </> (代码编辑器)图标。
  3. 将策略内容替换为以下策略:
<policies>
    <inbound>
        <base />
        <authentication-managed-identity
            resource="https://geocatalog.spatio.azure.com"
            client-id="<managed-identity-client-id>" />
        <set-header name="Accept-Encoding"
            exists-action="delete" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
        <find-and-replace
            from="https://<name>.<id>.<region>.geocatalog.spatio.azure.com"
            to="https://<apim-name>.azure-api.net" />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

替换以下占位符:

Placeholder 价值
<managed-identity-client-id> 分配给 APIM 的用户分配的托管标识的客户端 ID
<name>.<id>.<region> GeoCatalog 终结点组件
<apim-name> APIM 实例的名称

下表描述了每个策略元素:

政策要素 Purpose
authentication-managed-identity 使用指定的托管标识为 https://geocatalog.spatio.azure.com 受众获取令牌,然后将令牌附加到传出请求中。
set-header (删除 Accept-Encoding 从入站请求中删除 Accept-Encoding 请求头。 请参阅 为什么要删除 Accept-Encoding
find-and-replace 将响应正文中的 GeoCatalog 后端 URL 重写为 APIM 网关 URL。 如果不进行此重写,STAC 链接(selfrootparent)会将后端 URL 公开给调用方。

为什么删除 Accept-Encoding

默认情况下,Python requestshttpx 等客户端将发送 Accept-Encoding: gzip, deflate。 当后端收到此标头时,它将返回压缩的响应。 APIM 出站策略(如 find-and-replace 对原始响应正文进行操作)无法解压缩,因此它们无提示地不执行任何操作。 去除标头会强制后端返回出站策略可以处理的未压缩响应。

注释

curl 默认情况下 wget 不发送 Accept-Encoding 。 这意味着,在使用这些测试工具进行测试时,出站策略似乎正常工作。 不一致仅在请求压缩的客户端中出现。

强制实施集合级访问控制

默认情况下,GeoCatalog 向任何经过身份验证的调用方公开其所有集合。 若要限制哪些集合通过 APIM 可见,请应用操作级别策略来阻止广泛的 STAC 发现并强制实施允许列表。

定义允许的集合

在 APIM 中创建命名值以存储允许的集合 ID 列表:

  1. 在 APIM 实例中,从左侧栏中选择 “命名”值
  2. 选择+ 添加
  3. Name 设置为 allowed-collections.
  4. 设置为允许的集合 ID 的逗号分隔列表(例如, sentinel-2-l2a,landsat-8-c2-l2)。
  5. 选择“保存”

阻止登陆页和集合列表

阻止显示目录中每个集合的路由。 添加以下操作并附加立即返回 404 的策略:

显示名称 方法 URL 模板
阻止根目录 GET /
阻止集合 GET /stac/collections

将以下操作级别策略应用于这两个操作:

<policies>
    <inbound>
        <base />
        <return-response>
            <set-status code="404" reason="Not Found" />
        </return-response>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

STAC /stac/search 终结点接受 collections 参数 - 用作 GET 中的查询字符串或用于 POST 中的 JSON 正文。 如果没有限制措施,调用者可以搜索目录中的每个集合。 以下策略将验证是否仅请求来自允许集的集合。

添加两个操作:

显示名称 方法 URL 模板
GET 搜索 GET /stac/search
POST 搜索 POST /stac/search

GET /stac/search policy

此策略验证 collections 查询参数。 每个逗号分隔的值必须在允许的集合内。 不使用collections参数的请求将被拒绝。403 Forbidden

将以下策略应用于 GET 搜索 操作:

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var raw = context.Request.Url.Query
                    .GetValueOrDefault("collections", "");
                if (string.IsNullOrWhiteSpace(raw)) {
                    return true;
                }
                foreach (var c in raw.ToLower().Split(
                    new [] { "," },
                    StringSplitOptions.RemoveEmptyEntries))
                {
                    if (!c.Trim().Equals(allowed)) {
                        return true;
                    }
                }
                return false;
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

注释

APIM 策略表达式在受限的 C# 环境中运行。 使用 condition='@{...}' (单引号属性),因此双引号在表达式内工作。 避免使用泛型类型参数(例如 GetValueOrDefault<string>)和 LINQ lambda - 改用显式强制转换和 foreach 循环。

POST /stac/search 策略

此策略分析 JSON 正文并验证 collections 数组。 不使用collections参数的请求将被拒绝。403 Forbidden

将以下策略应用于 POST 搜索 操作:

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <set-variable name="requestBody"
            value="@(context.Request.Body
                .As&lt;string&gt;(
                    preserveContent: true))" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var body = (string)context
                    .Variables["requestBody"];
                var json = Newtonsoft.Json.Linq
                    .JObject.Parse(body);
                var arr = json["collections"]
                    as Newtonsoft.Json.Linq.JArray;
                if (arr == null || arr.Count == 0) {
                    return true;
                }
                foreach (var token in arr) {
                    if (!token.ToString().Trim()
                        .ToLower().Equals(allowed))
                    {
                        return true;
                    }
                }
                return false;
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

在集合终结点强制实施允许的集合

如果没有显式操作,请求(如 GET /stac/collections/sentinel-2-l2aGET /stac/collections/sentinel-2-l2a/items)会直接通过 GET /* 通配符到达后端,而不进行任何集合级检查。 将能够验证collection_id{{allowed-collections}}之间关系的路径参数策略应用到您在定义 API 操作中创建的以下操作:

显示名称 方法 URL 模板
获取单个集合 GET /stac/collections/{collection_id}
获取集合子资源 GET /stac/collections/{collection_id}/*
获取集合项 GET /stac/collections/{collection_id}/items

将以下策略应用于所有三个操作:

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var collectionId = (string)context
                    .Request.MatchedParameters[
                        "collection_id"];
                return !collectionId.Trim()
                    .ToLower().Equals(allowed);
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

在 SAS 令牌路由中强制实施允许的集合

GeoCatalog SAS API 允许调用方生成存储令牌并对资产 HREF 进行签名。 在没有任何限制的情况下,调用方可以获取任意集合的令牌。 以下策略确保只能访问允许的集合。

添加以下操作:

显示名称 方法 URL 模板
获取 SAS 令牌 GET /sas/token/{collection_id}
阻止 SAS 签名 GET /sas/sign

404 阻止策略(与 根块和集合块相同)应用于 阻止 SAS 签名 操作。 调用方应改用 /sas/token/{collection_id} 来获取集合级 SAS 令牌。

将以下策略应用于 GET SAS 令牌 操作:

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var collectionId = (string)context
                    .Request.MatchedParameters[
                        "collection_id"];
                return !collectionId.Trim()
                    .ToLower().Equals(allowed);
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

在数据路由中强制实施允许的集合

/data/mosaic/ 终结点提供图块渲染、边界框裁剪和搜索注册。 需要两个策略组:

  1. 寄存器搜索 - 验证 JSON 正文中的collections数组。
  2. 所有其他集合路由 - 验证 collectionId 路径参数。

添加以下操作:

显示名称 方法 URL 模板
POST 寄存器搜索 POST /data/mosaic/register
GET 数据收集 GET /data/mosaic/collections/{collectionId}/*

POST /data/mosaic/register 策略

此策略将验证 JSON 正文中的 collections 数组是否符合允许集的标准。 缺少 collections 参数的请求将被拒绝。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <set-variable name="requestBody"
            value="@(context.Request.Body
                .As&lt;string&gt;(
                    preserveContent: true))" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var body = (string)context
                    .Variables["requestBody"];
                var json = Newtonsoft.Json.Linq
                    .JObject.Parse(body);
                var arr = json["collections"]
                    as Newtonsoft.Json.Linq.JArray;
                if (arr == null || arr.Count == 0) {
                    return true;
                }
                foreach (var token in arr) {
                    if (!token.ToString().Trim()
                        .ToLower().Equals(allowed))
                    {
                        return true;
                    }
                }
                return false;
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

GET /data/mosaic/collections/{collectionId}/* 策略

此策略会对 collectionId 路径参数进行验证,以确保其属于许可的集合。 将此策略应用于 GET 数据收集 操作。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var collectionId = (string)context
                    .Request.MatchedParameters[
                        "collectionId"];
                return !collectionId.Trim()
                    .ToLower().Equals(allowed);
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

常见问题解答

如何更新允许的集合列表?

在 APIM 实例中编辑allowed-collections命名值。 不需要任何策略更改。

如果调用方省略集合参数,会发生什么情况?

请求被拒绝。403 Forbidden 调用方必须始终指定要搜索的集合。