除了對使用者進行驗證之外,您可能還需要允許其他服務與您的 API 互動。雖然用戶端應用程式可以提供網路登入提示給使用者,請使用者提交其憑證,但還需要另一種方法來進行安全的服務對服務通訊。此頁會介紹進行服務之間驗證的建議方式,並提供範例程式碼。
總覽
為了識別傳送要求至您 API 的服務,需要使用服務帳戶。呼叫服務會使用服務帳戶的私密金鑰來簽署安全的 JSON Web Token (JWT),並將要求中的已簽署 JWT 傳送給您的 API。
在 API 和呼叫服務內進行服務對服務的驗證:
- 建立呼叫服務要使用的服務帳戶與金鑰。
- 在 Cloud Endpoints 服務的 OpenAPI 文件新增驗證支援。
將程式碼新增到呼叫服務,如此就會:
- 建立 JWT 並以服務帳戶私密金鑰簽署。
- 在要求中將已簽署的 JWT 傳送至 API。
ESP 將要求轉送至您的 API 前,會先驗證 JWT 內的憑證附加資訊是否符合您 OpenAPI 文件的設定。ESP 不會檢查該服務帳戶已經獲准的 Cloud Identity 權限。
必要條件
本頁假設您已具備以下條件:
使用金鑰建立服務帳戶
您會需要一個服務帳戶,這個服務帳戶需擁有呼叫服務用來簽署 JWT 的私密金鑰檔案。如果您有多個服務會向您的 API 發送要求,則可建立一個服務帳戶來代表所有的呼叫服務。如果您需要區別不同服務 (例如各服務具有的權限可能各不相同),就可以為每個呼叫服務建立服務帳戶和金鑰。
本節會示範如何使用 Google Cloud 控制台與 gcloud 指令列工具來建立服務帳戶與私密金鑰檔案,並將服務帳戶憑證建立者角色指派給服務帳戶。如要瞭解如何使用 API 進行這項工作,請參閱「建立和管理服務帳戶」。
如要建立服務帳戶和金鑰,請按照下列步驟操作:
Google Cloud 控制台
建立服務帳戶:
前往 Google Cloud 控制台的「建立服務帳戶」頁面。
選取要使用的專案。
在 [Service account name] (服務帳戶名稱) 欄位中輸入一個名稱。
選用:在「服務帳戶說明」欄位中輸入說明。
點選「建立」。
按一下 [完成]。
請勿關閉瀏覽器視窗。您將在下一個步驟中使用此項目。
建立服務帳戶金鑰:
- 在 Google Cloud 控制台中,按一下您建立的服務帳戶電子郵件地址。
- 點選「金鑰」。
- 依序點選「新增金鑰」和「建立新的金鑰」。
- 點按「Create」(建立)。隨後一個包含服務帳戶私密金鑰的 JSON 檔案就會下載到電腦中。
- 按一下 [關閉]。
gcloud
您可以使用本機電腦上的 Google Cloud CLI 或在 Cloud Shell 內執行下列指令。
設定
gcloud的預設帳戶。如果您有多個帳戶,請確認所選擇的帳戶位於您要使用的 Google Cloud 專案中。gcloud auth login顯示 Google Cloud 專案的專案 ID。
gcloud projects list設定預設專案。將
PROJECT_ID替換為您要使用的 Google Cloud 專案 ID。gcloud config set project PROJECT_ID
建立服務帳戶。將
SA_NAME和SA_DISPLAY_NAME替換為您要使用的名稱和顯示名稱。gcloud iam service-accounts create SA_NAME \ --display-name "SA_DISPLAY_NAME"
顯示剛剛建立的服務帳戶的電子郵件地址。
gcloud iam service-accounts list新增服務帳戶憑證建立者角色。將
SA_EMAIL_ADDRESS替換成服務帳戶的電子郵件地址。gcloud projects add-iam-policy-binding PROJECT_ID \ --member serviceAccount:SA_EMAIL_ADDRESS \ --role roles/iam.serviceAccountTokenCreator
在目前的工作目錄中建立服務帳戶金鑰檔案。將
FILE_NAME替換為您要用於金鑰檔案的名稱。根據預設,gcloud指令會建立一個 JSON 檔案。gcloud iam service-accounts keys create FILE_NAME.json \ --iam-account SA_EMAIL_ADDRESS
如要進一步瞭解上述指令,請參閱 gcloud 參考資料。
如需私密金鑰保護措施的相關資訊,請參閱管理憑證的最佳做法。
設定 API 以支援驗證
如要為呼叫閘道的服務啟用服務帳戶驗證,請修改 OpenAPI 文件中的安全物件,讓 ESP 驗證已簽署 JWT 中的憑證附加資訊。修改內容會因使用的 OpenAPI 規格版本而異。
OpenAPI 2.0
- 在 OpenAPI 規格中將服務帳戶新增為發行者:
securityDefinitions: DEFINITION_NAME: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "SA_EMAIL_ADDRESS" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/SA_EMAIL_ADDRESS"
- 將
DEFINITION_NAME替換成可用來識別此安全性定義的字串。建議將其替換成服務帳戶名稱或是能辨別呼叫服務的名稱。 - 將
SA_EMAIL_ADDRESS替換成服務帳戶的電子郵件地址。 - 您可以在 OpenAPI 規範中定義多項安全定義,但是每項定義必須要有不同的
x-google-issuer。如果您為每個呼叫服務建立了個別的服務帳戶,則可以再為每個服務帳戶建立安全性定義,例如:securityDefinitions: service-1: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "service-1@example-project-12345.iam.gserviceaccount.com" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-1@example-project-12345.iam.gserviceaccount.com" service-2: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "service-2@example-project-12345.iam.gserviceaccount.com" x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-2@example-project-12345.iam.gserviceaccount.com"
- 將
- 也可以將
x-google-audiences新增至securityDefinitions區段。如果您沒有新增x-google-audiences,ESP 會要求 JWT 中的"aud"(目標對象) 憑證附加資訊格式為https://SERVICE_NAME,其中 SERVICE_NAME 是您的 ESP 服務名稱 (您已在 OpenAPI 文件的host欄位中設定的名稱)。 - 在檔案頂層 (非縮排或巢狀結構) 新增一個
security區段,並套用至整個 API,或是在方法層中套用至特定方法。如果您在 API 層級和方法層級使用security區段,方法層級的設定就會覆寫 API 層級的設定。security: - DEFINITION_NAME: []
- 將
DEFINITION_NAME替換成您在securityDefinitions區段中使用的名稱。 - 如果您在
securityDefinitions區段中有多個定義,請將其新增至security區段,例如:security: - service-1: [] - service-2: []
- 將
- 部署更新後的 OpenAPI 規格。在 ESP 向您的 API 轉送要求之前,ESP 會驗證以下項目:
- 使用公開金鑰的 JWT 簽署,位於 OpenAPI 規格中
x-google-jwks_uri欄位指定的 URI。 - JWT 中的
"iss"(發行者) 憑證附加資訊與x-google-issuer欄位指定的值是否相符。 - JWT 中的
"aud"(目標對象) 憑證附加資訊是否含有您的 ESP 服務名稱,或是否與x-google-audiences欄位指定的其中一個值相符。 - 使用
"exp"(到期時間) 憑證附加資訊,確認憑證並未過期。
- 使用公開金鑰的 JWT 簽署,位於 OpenAPI 規格中
OpenAPI 3.x
- 在 OpenAPI 規格中將服務帳戶新增為發行者:
components: securitySchemes: SCHEME_NAME: type: oauth2 flows: implicit: authorizationUrl: "" scopes: {} x-google-auth: issuer: SA_EMAIL_ADDRESS jwksUri: https://www.googleapis.com/robot/v1/metadata/x509/SA_EMAIL_ADDRESS audiences: - 848149964201.apps.googleusercontent.com - 841077041629.apps.googleusercontent.com jwtLocations: - header: Authorization valuePrefix: "Bearer " security: - SCHEME_NAME: []
- 將
SCHEME_NAME替換成可用來識別此安全配置的字串。建議將其替換成服務帳戶名稱或是能辨別呼叫服務的名稱。 - 將
SA_EMAIL_ADDRESS替換成服務帳戶的電子郵件地址。 - 您可以在 OpenAPI 規格中定義多項安全配置,但是每項定義必須要有不同的
issuer。如果您為每個呼叫服務建立了個別的服務帳戶,則可以再為每個服務帳戶建立安全性定義,例如:components: securitySchemes: service-1: type: oauth2 flows: implicit: authorizationUrl: "" scopes: {} x-google-auth: issuer: "service-1@example-project-12345.iam.gserviceaccount.com" jwksUri: https://www.googleapis.com/robot/v1/metadata/x509/service-1@example-project-12345.iam.gserviceaccount.com jwtLocations: - header: Authorization valuePrefix: "Bearer " service-2: type: oauth2 flows: implicit: authorizationUrl: "" scopes: {} x-google-auth: issuer: "service-2@example-project-12345.iam.gserviceaccount.com" jwksUri: "https://www.googleapis.com/robot/v1/metadata/x509/service-2@example-project-12345.iam.gserviceaccount.com" jwtLocations: - header: Authorization valuePrefix: "Bearer "
- 將
- 也可以將
audiences新增至securitySchemes區段。如果您沒有新增audiences,ESP 會要求 JWT 中的"aud"(目標對象) 憑證附加資訊格式為https://SERVICE_NAME,其中 SERVICE_NAME 是您的 ESP 服務名稱 (您已在 OpenAPI 文件的host欄位中設定的名稱)。 - 在檔案頂層 (非縮排或巢狀結構) 新增一個
security區段,並套用至整個 API,或是在方法層中套用至特定方法。如果您在 API 層級和方法層級使用security區段,方法層級的設定就會覆寫 API 層級的設定。security: - SCHEME_NAME: []
- 將 SCHEME_NAME 替換成您在
securitySchemes區段中使用的名稱。 - 如果您在
securitySchemes區段中有多個定義,請將其新增至security區段,例如:security: - service-1: [] - service-2: []
- 將 SCHEME_NAME 替換成您在
- 部署更新後的 OpenAPI 規格。在 ESP 向您的 API 轉送要求之前,ESP 會驗證以下項目:
- 使用公開金鑰的 JWT 簽署,位於 OpenAPI 規格中
jwksUri欄位指定的 URI。 - JWT 中的
"iss"(發行者) 憑證附加資訊與issuer欄位指定的值是否相符。 - JWT 中的
"aud"(目標對象) 憑證附加資訊是否含有您的 ESP 服務名稱,或是否與audiences欄位指定的其中一個值相符。 - 使用
"exp"(到期時間) 憑證附加資訊,確認憑證並未過期。
- 使用公開金鑰的 JWT 簽署,位於 OpenAPI 規格中
向 Endpoints API 發出經過驗證的要求
為了發出經過驗證的要求,呼叫服務會傳送您在 OpenAPI 文件中指定的服務帳戶簽署的 JWT。呼叫服務必須:
- 建立一個 JWT,並以服務帳戶私密金鑰簽署。
- 在要求中將簽署的 JWT 傳送至 API。
以下範例程式碼示範了部分語言的這項程序。如要以其他語言提出已驗證的要求,請參閱 jwt.io,瞭解支援的程式庫清單。
- 在呼叫服務中,新增以下函式,並將以下參數傳送給函式:
Java saKeyfile:服務帳戶私密金鑰檔案的完整路徑。saEmail:服務帳戶的電子郵件地址。-
audience:如果已在 OpenAPI 文件中新增了x-google-audiences欄位,請將audience設定為您指定給x-google-audiences的其中一個值。或是將audience設定為https://SERVICE_NAME,其中SERVICE_NAME是您的 Endpoints 服務名稱。 expiryLength:JWT 的到期時間,以秒為單位。
Python sa_keyfile:服務帳戶私密金鑰檔案的完整路徑。sa_email:服務帳戶的電子郵件地址。-
audience:如果已在 OpenAPI 文件中新增了x-google-audiences欄位,請將audience設定為您指定給x-google-audiences的其中一個值。或是將audience設定為https://SERVICE_NAME,其中SERVICE_NAME是您的 Endpoints 服務名稱。 expiry_length:JWT 的到期時間,以秒為單位。
Go saKeyfile:服務帳戶私密金鑰檔案的完整路徑。saEmail:服務帳戶的電子郵件地址。-
audience:如果已在 OpenAPI 文件中新增了x-google-audiences欄位,請將audience設定為您指定給x-google-audiences的其中一個值。或是將audience設定為https://SERVICE_NAME,其中SERVICE_NAME是您的 Endpoints 服務名稱。 expiryLength:JWT 的到期時間,以秒為單位。
此函式會建立一個 JWT,並使用私密金鑰檔案來簽署 JWT,然後將已簽署的 JWT 傳回。
Java Python Go - 在呼叫服務中,新增下列函式,以在要求中使用
Authorization: Bearer標頭傳送已簽署的 JWT 給 API:Java Python Go
當您使用 JWT 傳送一項要求時,基於安全考量,建議您將驗證憑證放入 Authorization: Bearer 標頭之中。例如:
curl --request POST \
--header "Authorization: Bearer ${TOKEN}" \
"${ENDPOINTS_HOST}/echo"
其中 ENDPOINTS_HOST 和 TOKEN 分別是內含您 API 主機名稱和 API 驗證憑證的環境變數。
在 API 中接收驗證結果
ESP 通常會轉發所有接收到的標頭。不過,如果後端位址是由 OpenAPI 規格中的 x-google-backend 或 gRPC 服務設定中的 BackendRule 指定,系統就會覆寫原始的 Authorization 標頭。
ESP 會將 X-Endpoint-API-UserInfo 中的驗證結果傳送至後端 API。建議您使用這個標頭,取代原始的 Authorization 標頭。這個標頭是字串,會 base64url 編碼 JSON 物件。ESPv2 和 ESP 的 JSON 物件格式不同。
如果是 ESPv2,JSON 物件就是原始 JWT 酬載。如果是 ESP,JSON 物件會使用不同的欄位名稱,並將原始 JWT 酬載放在 claims 欄位下。如要進一步瞭解格式,請參閱「在後端服務中處理 JWT」。