本页介绍如何从用户账号或服务账号向受 Identity-Aware Proxy (IAP) 保护的资源进行身份验证。
用户账号属于个别用户。当您的应用需要代表用户访问受 IAP 保护的资源,则对用户账号进行身份验证。如需了解详情,请参阅用户账号。
服务账号属于应用而非个别用户。 如果要允许应用访问受 IAP 保护的资源,则对服务账号进行身份验证。如需了解详情,请参阅服务账号。
准备工作
在开始之前,您需要做好以下准备:
- 要使用开发者账号、服务账号或移动应用凭据以编程方式连接并且受 IAP 保护的应用。
对用户账号进行身份验证
您可以允许用户从桌面应用或移动应用访问您的应用,从而支持程序与受 IAP 保护的资源进行交互。
从移动应用进行身份验证
- 为您的移动应用创建或使用现有的 OAuth 2.0 客户端 ID。如需使用现有的 OAuth 2.0 客户端 ID,请按照如何共享 OAuth 客户端中的步骤操作。
- 将 OAuth 客户端 ID 列入许可名单,以便为应用提供程序化访问权限。
- 为受 IAP 保护的客户端 ID 获取一个 ID 令牌。
- Android:使用 Google Sign-In API 请求 OpenID Connect (OIDC) 令牌。将
requestIdToken
客户端 ID 设置为您要连接的资源的客户端 ID。 - iOS:使用 Google 登录获取 ID 令牌。
- Android:使用 Google Sign-In API 请求 OpenID Connect (OIDC) 令牌。将
- 在
Authorization: Bearer
标头中包含 ID 令牌以向受 IAP 保护的资源发出经过身份验证的请求。
从桌面应用进行身份验证
本部分介绍如何使用桌面命令行对用户账号进行身份验证。
- 如需允许开发者使用命令行访问您的应用,请创建桌面版 OAuth 2.0 客户端 ID 或共享现有的桌面版 OAuth 客户端 ID。
- 将 OAuth 客户端 ID 列入许可名单,以便为应用以编程方式访问。
登录应用
每个想要访问受 IAP 保护的应用的开发者都需要先登录。您可以使用 gcloud CLI 等工具将流程打包到脚本中。以下示例演示了如何使用 curl 登录并生成一个可用于访问应用的令牌:
- 登录到有权访问 Google Cloud 资源的账号。
-
启动一个可以回显传入请求的本地服务器。
注意:该命令使用 NetCat 实用程序。 您可以使用自己偏好的实用程序。$ nc -k -l 4444
-
前往以下 URI,其中
DESKTOP_CLIENT_ID
是桌面应用客户端 ID:https://accounts.google.com/o/oauth2/v2/auth?client_id=DESKTOP_CLIENT_ID&response_type=code&scope=openid%20email&access_type=offline&redirect_uri=http://localhost:4444&cred_ref=true
-
在本地服务器输出中,查找请求参数。您应该会看到类似以下内容:
GET /?code=$CODE&scope=email%20openid%20https://www.googleapis.com/auth/userinfo.email&hd=google.com&prompt=consent HTTP/1.1
复制代码,以替换下面的AUTH_CODE
以及桌面应用客户端 ID 和密钥:curl --verbose \ --data client_id=DESKTOP_CLIENT_ID \ --data client_secret=DESKTOP_CLIENT_SECRET \ --data code=AUTH_CODE \ --data redirect_uri=http://localhost:4444 \ --data grant_type=authorization_code \ https://oauth2.googleapis.com/token
此代码会返回一个带有
id_token
字段的 JSON 对象,您可以使用该字段访问应用。
访问应用
如需访问该应用,请使用 id_token
,如下所示:
curl --verbose --header 'Authorization: Bearer ID_TOKEN' URL
刷新令牌
您可以使用在登录流程中生成的刷新令牌来获取新的 ID 令牌。当原始 ID 令牌过期时,这非常有用。每个 ID 令牌的有效期约为一小时,在此期间,您可以向特定应用发出多个请求。
以下示例演示了如何使用 curl 使用刷新令牌获取新的 ID 令牌。在以下示例中,REFRESH_TOKEN
是登录流程中的令牌。DESKTOP_CLIENT_ID
和 DESKTOP_CLIENT_SECRET
与登录流程中使用的相同:
curl --verbose \ --data client_id=DESKTOP_CLIENT_ID \ --data client_secret=DESKTOP_CLIENT_SECRET \ --data refresh_token=REFRESH_TOKEN \ --data grant_type=refresh_token \ https://oauth2.googleapis.com/token
此代码会返回一个带有新的 id_token
字段的 JSON 对象,您可以使用该字段访问应用。
对服务账号进行身份验证
您可以使用服务账号 JWT 或 OpenID Connect (OIDC) 令牌向受 IAP 保护的资源对服务账号进行身份验证。下表概述了不同身份验证令牌及其功能之间的一些差异。
Authentication 功能 | 服务账号 JWT | OpenID Connect 令牌 |
---|---|---|
情境感知访问权限支持 | ||
OAuth 2.0 客户端 ID 要求 | ||
令牌作用域 | 受 IAP 保护的资源的网址 | OAuth 2.0 客户端 ID |
使用服务账号 JWT 进行身份验证
使用 JWT 对服务账号进行身份验证包含以下主要步骤:
向发起调用的服务账号授予 Service Account Token Creator 角色 (
roles/iam.serviceAccountTokenCreator
)。该角色会授予主账号创建短期有效凭据(例如 JWT)的权限。
为受 IAP 保护的资源创建 JWT。
使用服务账号私钥对 JWT 进行签名。
创建 JWT
创建的 JWT 应具有类似于以下示例的载荷:
{ "iss": SERVICE_ACCOUNT_EMAIL_ADDRESS, "sub": SERVICE_ACCOUNT_EMAIL_ADDRESS, "aud": TARGET_URL, "iat": IAT, "exp": EXP, }
对于
iss
和sub
字段,请指定服务账号的电子邮件地址。此值位于服务账号 JSON 文件的client_email
字段中,或通过传入方式提供。典型格式:service-account@PROJECT_ID.iam.gserviceaccount.com
对于
aud
字段,请指定受 IAP 保护的资源的网址。对于
iat
字段,请指定当前的 Unix 纪年时间;对于exp
字段,请指定 3600 秒后的某个时间。这定义了 JWT 的过期时间。
为 JWT 签名
您可以使用以下任一方法为 JWT 签名:
- 使用 IAM 凭据 API 为 JWT 签名,而无需直接访问私钥。
- 使用本地凭据密钥文件在本地为 JWT 签名。
使用 IAM Service Account Credentials API 为 JWT 签名
使用 IAM Service Account Credentials API 为服务账号 JWT 签名。该方法会提取与您的服务账号关联的私钥,并使用该私钥对 JWT 载荷进行签名。这样,您无需直接访问私钥即可对 JWT 进行签名。
如需向 IAP 进行身份验证,请设置应用默认凭据。如需了解详情,请参阅为本地开发环境设置身份验证。
gcloud
运行以下命令,准备包含 JWT 载荷的请求:
cat > claim.json << EOM { "iss": "SERVICE_ACCOUNT_EMAIL_ADDRESS", "sub": "SERVICE_ACCOUNT_EMAIL_ADDRESS", "aud": "TARGET_URL", "iat": $(date +%s), "exp": $((`date +%s` + 3600)) } EOM
使用以下 Google Cloud CLI 命令对
request.json
中的载荷进行签名:gcloud iam service-accounts sign-jwt --iam-account=SERVICE_ACCOUNT_EMAIL_ADDRESS claim.json output.jwt
请求成功后,
output.jwt
会包含已签名的 JWT。使用 JWT 访问受 IAP 保护的资源。
Python
import datetime
import json
import google.auth
from google.cloud import iam_credentials_v1
import jwt
def generate_jwt_payload(service_account_email: str, resource_url: str) -> str:
"""Generates JWT payload for service account.
The resource url provided must be the same as the url of the IAP secured resource.
Args:
service_account_email (str): Specifies service account JWT is created for.
resource_url (str): Specifies scope of the JWT, the URL that the JWT will be allowed to access.
Returns:
A signed-jwt that can be used to access IAP protected applications.
Access the application with the JWT in the Authorization Header.
curl --verbose --header 'Authorization: Bearer SIGNED_JWT' URL
"""
iat = datetime.datetime.now(tz=datetime.timezone.utc)
exp = iat + 3600
return json.dumps({
'iss': service_account_email,
'sub': service_account_email,
'aud': resource_url,
'iat': iat,
'exp': exp,
})
def sign_jwt(target_sa: str, resource_url: str) -> str:
"""Signs JWT payload using ADC and IAM credentials API.
Args:
target_sa (str): Service Account JWT is being created for.
iap.webServiceVersions.accessViaIap permission is required.
resource_url (str): Audience of the JWT, and scope of the JWT token.
This is the url of the IAP protected application.
Returns:
A signed-jwt that can be used to access IAP protected apps.
"""
source_credentials, _ = google.auth.default()
iam_client = iam_credentials_v1.IAMCredentialsClient(credentials=source_credentials)
return iam_client.sign_jwt(
name=iam_client.service_account_path('-', target_sa),
payload=generate_jwt_payload(target_sa, resource_url),
).signed_jwt
请求成功后,脚本会返回已签名的 JWT。使用 JWT 访问受 IAP 保护的资源。
curl
运行以下命令,准备包含 JWT 载荷的请求:
cat << EOF > request.json { "payload": JWT_PAYLOAD } EOF
使用 IAM Service Account Credentials API 为 JWT 签名:
curl -X POST \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ -H "Content-Type: application/json; charset=utf-8" \ -d @request.json \ "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT_EMAIL_ADDRESS:signJwt"
请求成功后,响应中会包含已签名的 JWT。
使用 JWT 访问受 IAP 保护的资源。
使用本地凭据密钥文件为 JWT 签名
JWT 使用服务账号的私钥进行签名。
如果您有服务账号密钥文件,则可以在本地为 JWT 签名。
该脚本会随载荷一起发送 JWT 标头。对于标头中的 kid
字段,请使用服务账号的私钥 ID,该 ID 位于服务账号凭据 JSON 文件的 private_key_id
字段中。该密钥还用于为 JWT 签名。
Python
import time
import jwt
import json
def generate_jwt_payload(service_account_email, resource_url):
"""Generates JWT payload for service account.
The resource url provided must be the same as the url of the IAP secured resource.
Args:
service_account_email (str): Specifies service account JWT is created for.
resource_url (str): Specifies scope of the JWT, the URL that the JWT will be allowed to access.
Returns:
A signed-jwt that can be used to access IAP protected applications.
Access the application with the JWT in the Authorization Header.
curl --verbose --header 'Authorization: Bearer SIGNED_JWT' URL
"""
iat = datetime.datetime.now(tz=datetime.timezone.utc)
exp = iat + 3600
return json.dumps({
'iss': service_account_email,
'sub': service_account_email,
'aud': resource_url,
'iat': iat,
'exp': exp,
})
def sign_jwt_with_key_file(credential_key_file_path, resource_url):
"""Signs JWT payload using local service account credential key file.
Args:
credential_key_file_path (str): Path to the downloaded JSON credentials of the service
account the JWT is being created for.
resource_url (str): Scope of JWT token, This is the url of the IAP protected application.
Returns:
A service account JWT created with a downloaded private key.
"""
with open(credential_key_file_path, 'r') as credential_key_file:
key_data = json.load(credential_key_file)
PRIVATE_KEY_ID_FROM_JSON = key_data["private_key_id"]
PRIVATE_KEY_FROM_JSON = key_data["private_key"]
SERVICE_ACCOUNT_EMAIL = key_data["client_email"]
# Sign JWT with private key and store key id in the header
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
payload = generate_jwt_payload(service_account_email=SERVICE_ACCOUNT_EMAIL, resource_url=resource_url)
signed_jwt = jwt.encode(
payload,
PRIVATE_KEY_FROM_JSON,
headers=additional_headers,
algorithm='RS256',
)
return signed_jwt
结果是已签名的 JWT。
访问应用
在所有情况下,如需访问该应用,请使用 signed-jwt
,如下所示:
curl --verbose --header 'Authorization: Bearer SIGNED_JWT' URL
使用 OIDC 令牌进行身份验证
- 创建或使用现有的 OAuth 2.0 客户端 ID。如需使用现有的 OAuth 2.0 客户端 ID,请按照如何共享 OAuth 客户端中的步骤操作。
- 将 OAuth 客户端 ID 列入许可名单,以便为应用提供程序化访问权限。
您还需要将服务账号添加到受 IAP 保护的项目的访问列表中。以下代码示例显示了如何获取 OIDC 令牌。您必须在 Authorization: Bearer
标头中添加令牌,以便向受 IAP 保护的资源发出身份验证请求。
获取默认服务账号的 OIDC 令牌
如果您希望为 Compute Engine、App Engine 或 Cloud Run 的默认服务账号获取 OIDC 令牌,则可以使用以下代码示例生成令牌,以访问受 IAP 保护的资源:
C#
Go
如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证。
Java
Node.js
PHP
如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证。
Python
如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证。
Ruby
如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证。
从本地服务账号密钥文件获取 OIDC 令牌
如果您有服务账号密钥文件,则可以调整上述代码示例以提供服务账号密钥文件。
Bash
#!/usr/bin/env bash
set -euo pipefail
get_token() {
# Get the bearer token in exchange for the service account credentials.
local service_account_key_file_path="${1}"
local iap_client_id="${2}"
local iam_scope="https://www.googleapis.com/auth/iam"
local oauth_token_uri="https://www.googleapis.com/oauth2/v4/token"
local private_key_id="$(cat "${service_account_key_file_path}" | jq -r '.private_key_id')"
local client_email="$(cat "${service_account_key_file_path}" | jq -r '.client_email')"
local private_key="$(cat "${service_account_key_file_path}" | jq -r '.private_key')"
local issued_at="$(date +%s)"
local expires_at="$((issued_at + 600))"
local header="{'alg':'RS256','typ':'JWT','kid':'${private_key_id}'}"
local header_base64="$(echo "${header}" | base64)"
local payload="{'iss':'${client_email}','aud':'${oauth_token_uri}','exp':${expires_at},'iat':${issued_at},'sub':'${client_email}','target_audience':'${iap_client_id}'}"
local payload_base64="$(echo "${payload}" | base64)"
local signature_base64="$(printf %s "${header_base64}.${payload_base64}" | openssl dgst -binary -sha256 -sign <(printf '%s\n' "${private_key}") | base64)"
local assertion="${header_base64}.${payload_base64}.${signature_base64}"
local token_payload="$(curl -s \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
--data-urlencode "assertion=${assertion}" \
https://www.googleapis.com/oauth2/v4/token)"
local bearer_id_token="$(echo "${token_payload}" | jq -r '.id_token')"
echo "${bearer_id_token}"
}
main(){
# TODO: Replace the following variables:
SERVICE_ACCOUNT_KEY="service_account_key_file_path"
IAP_CLIENT_ID="iap_client_id"
URL="application_url"
# Obtain the ID token.
ID_TOKEN=$(get_token "${SERVICE_ACCOUNT_KEY}" "${IAP_CLIENT_ID}")
# Access the application with the ID token.
curl --header "Authorization: Bearer ${ID_TOKEN}" "${URL}"
}
main "$@"
在所有其他情况下获取 OIDC 令牌
在所有其他情况下,请在访问受 IAP 保护的资源之前,通过模拟目标服务账号,使用 IAM Credentials API 生成 OIDC 令牌。此过程包括以下步骤:
为发起调用的服务账号(与获取 ID 令牌的代码关联的服务账号)提供 Service Account OpenID Connect Identity Token Creator 角色 (
roles/iam.serviceAccountOpenIdTokenCreator
)。这样一来,调用方服务账号便可以模拟目标服务账号。
使用调用服务账号提供的凭据对目标服务账号调用 generateIdToken 方法。
将
audience
字段设置为您的客户端 ID。
如需查看分步说明,请参阅创建 ID 令牌。
从 Proxy-Authorization 标头进行身份验证
如果您的应用使用 Authorization
请求标头,您可以改为在 Proxy-Authorization: Bearer
标头中添加 ID 令牌。如果在 Proxy-Authorization
标头中找到了有效的 ID 令牌,则 IAP 会用它授权请求。授权请求后,IAP 会将 Authorization
标头传递给您的应用,而不处理内容。
如果在 Proxy-Authorization
标头中未找到有效的 ID 令牌,则 IAP 会继续处理 Authorization
标头并在将请求传递给应用之前去除 Proxy-Authorization
标头。
后续步骤
- 详细了解 Authorization: Bearer 令牌。
- 尝试 Android 登录或 iOS 登录。