本文將引導你如何在 Microsoft Planetary Computer Pro GeoCatalog 前設置 Azure API 管理 (APIM)作為 API 代理。 透過這種配置,你可以:
- 啟用匿名存取:來電者不需要自己的 Microsoft Entra 憑證。 APIM 會代表他們使用管理身份向 GeoCatalog 進行認證。
- 非 Entra 認證:來電者可支援非 Entra 的認證方法。 APIM 代表他們使用 Managed ID 向 GeoCatalog 進行認證。
- 強制執行收藏層級存取控制:限制透過代理可見的時空存取目錄(STAC)集合,即使地理目錄原生不支援集合層級的角色基礎存取控制(RBAC)。
下圖展示了加入 APIM 代理前後的架構:
之前 每位來電者都直接向 GeoCatalog 進行認證:
caller ──(Entra token)──► GeoCatalog
之後 APIM 位於呼叫者與 GeoCatalog 之間,負責認證與存取控制:
caller ──(anonymous / APIM Subscription Keys)──► APIM ──(managed identity token)──► GeoCatalog
先決條件
- 一個有有效訂閱的 Azure 帳號。 免費建立帳戶。
- 現有的 GeoCatalog 資源。
- 一個 Azure API 管理實例。
- 指派給 APIM 執行個體的使用者指派受控識別,並已在 GeoCatalog 資源上獲得 GeoCatalog 讀者角色指派。 請參閱 管理存取 權以了解角色指派的說明。
將受管理身份指派給 APIM
在 APIM 能認證你的 GeoCatalog 之前,你需要將使用者指派的管理身份與 APIM 實例關聯。
- 在 Azure 入口網站中,流覽至您的 API 管理實例。
- 從左側邊欄選擇 「身份 」。
- 選取 使用者指派的 索引標籤。
- 選取 [新增],然後選擇在 GeoCatalog 上具有 GeoCatalog 讀者角色的使用者指派受控識別。
- 選取 [新增] 並確認。
在 APIM 中建立 API
在 APIM 中定義一個新的 API,將請求代理到你的 GeoCatalog 後端。
在你的 APIM 實例中,從左側邊欄選擇 API 。
選擇 + 新增 API>HTTP。
請依以下設定配置 API:
Setting 價值 顯示名稱 描述性名稱(例如, GeoCatalog API)Web 服務 URL 你的 GeoCatalog 端點(例如, https://<name>.<id>.<region>.geocatalog.spatio.azure.com)URL 協議 HTTPS API URL 尾碼 留空(根路徑) 需訂閱 不,匿名存取;是,訂閱金鑰存取 選取 ,創建。
定義 API 操作
新增以下操作以匹配 GeoCatalog API 表面。 萬用卡(/*)操作會將所有匹配的請求轉發到後端。 明確的收集操作讓你能在之後套用特定收集的政策來控制存取。
| 顯示名稱 | 方法 | 網址範本 |
|---|---|---|
| GET | GET | /* |
| 取得收藏品 | GET | /stac/collections/{collection_id}/items |
| 取得單個資料集 | GET | /stac/collections/{collection_id} |
| 取得集合子資源 | GET | /stac/collections/{collection_id}/* |
| 郵件 | 郵件 | /* |
要新增每個作業:
- 選擇你建立的 API。
- 選取 [+ 新增作業]。
- 從前述表格輸入 顯示名稱、 方法和 網址範本 。
- 選取 [儲存]。
設定 API 層級政策
API 層級政策負責整個 API 的認證與 URL 重寫。 此策略會從使用者指定的管理身份取得一個令牌,並將其附加到每個轉發到 GeoCatalog 後端伺服器的請求。
- 選擇你建立的 API,然後選擇 所有操作。
- 在 [傳入處理 ] 區段中,選取 </> (程式碼編輯器) 圖示。
- 請將政策內容替換為以下政策:
<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>
取代下列預留位置:
| 預留位置 | 價值 |
|---|---|
<managed-identity-client-id> |
指派給 APIM 之使用者指派受控識別的用戶端識別碼 |
<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 連結(self、 root、 parent、等)會將後端 URL 暴露給呼叫者。 |
為什麼要移除 Accept-Encoding
Python requests 和 httpx 等用戶端預設會傳送 Accept-Encoding: gzip, deflate。 當後端接收到此標頭時,會回傳壓縮後的回應。 APIM 的輸出策略,例如 find-and-replace,在原始回應體上運作,無法解壓縮,因此不會執行任何操作。 移除標頭會導致後端回傳未壓縮的回應,外發策略可以處理該回應。
Note
curl 和 wget 默認不發送 Accept-Encoding。 這表示當你用這些工具測試時,外發政策似乎能正常運作。 這種不一致只有在客戶端要求壓縮時才會顯現。
強制執行集合層級的存取控制
預設情況下,GeoCatalog 會向任何認證呼叫者公開其所有收藏。 若要限制可透過 APIM 看見的集合,請套用作業層級原則,以封鎖廣泛的 STAC 探索並強制執行允許清單。
定義允許的集合
在 APIM 中建立一個命名值來儲存允許的集合 ID 清單:
- 在你的 APIM 實例中,從左側邊欄選擇 命名值 。
- 選取 [+ 新增]。
- 將 名稱 設為
allowed-collections。 - 將 Value 設定為一個逗號分隔的允許集合 ID 清單(例如
sentinel-2-l2a,landsat-8-c2-l2)。 - 選取 [儲存]。
封鎖登陸頁面和收藏列表
封鎖會顯示目錄中每個集合的路由。 新增以下操作並附加一個能立即回傳 404 的政策:
| 顯示名稱 | 方法 | 網址範本 |
|---|---|---|
| 區塊根 | 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 /stac/search 端點接受 collections 參數——在 GET 時作為查詢字串,或在 POST 的 JSON 主體中。 如果沒有防護機制,呼叫端可能會搜尋目錄中的每個集合。 下列原則會驗證只要求允許集合中的集合。
新增兩個運算:
| 顯示名稱 | 方法 | 網址範本 |
|---|---|---|
| GET 搜尋 | GET | /stac/search |
| POST 搜尋 | 郵件 | /stac/search |
GET /stac/search 原則
此策略會驗證查詢參數。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>
Note
APIM 政策表達式在受限的 C# 環境中執行。 使用 condition='@{...}' (單引號屬性)讓雙引號能在表達式內運作。 避免使用通用型別參數(例如 GetValueOrDefault<string>)和 LINQ lambda,改用明確的 cast 和 foreach loop。
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<string>(
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-l2a 或 GET /stac/collections/sentinel-2-l2a/items 等請求將落入 GET /* 通配符,並抵達後端而不需進行集合層級檢查。 將驗證collection_id{{allowed-collections}}的路徑參數政策套用到你在定義 API 操作中建立的以下操作:
| 顯示名稱 | 方法 | 網址範本 |
|---|---|---|
| 取得單一集合 | 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。 在沒有任何限制的情況下,呼叫者可以為任何收藏取得代幣。 以下政策確保僅能存取允許的資料集。
加入以下運算:
| 顯示名稱 | 方法 | 網址範本 |
|---|---|---|
| 取得 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/ 端點提供圖格轉譯、周框裁切和搜尋註冊。 需要兩個政策小組:
-
寄存器搜尋 ——驗證
collectionsJSON 主體中的陣列。 -
其他收集路由,驗證
collectionId路徑參數。
加入以下運算:
| 顯示名稱 | 方法 | 網址範本 |
|---|---|---|
| POST 註冊搜尋 | 郵件 | /data/mosaic/register |
| GET 資料收集 | GET | /data/mosaic/collections/{collectionId}/* |
POST /data/mosaic/register 原則
此政策會驗證 collections JSON 內容中的陣列是否符合允許的集合。 沒有參數 collections 的請求會被拒絕。
<policies>
<inbound>
<base />
<set-variable name="allowedCsv"
value="{{allowed-collections}}" />
<set-variable name="requestBody"
value="@(context.Request.Body
.As<string>(
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>
常見問題
我該如何更新允許的收藏清單?
編輯 allowed-collections APIM 實例中的命名值。 不需要任何政策變更。
如果呼叫者遺漏了集合參數會發生什麼事?
請求被拒絕,顯示 403 Forbidden。 呼叫者必須始終指定他們想搜尋的集合。