如何使用 python 以编程方式获取 GCP Bearer 令牌
How to get a GCP Bearer token programmatically with python
gcloud auth print-access-token
给了我一个我以后可以使用的 Bearer 令牌;然而,这是一个 shell 命令。我如何通过 Google Cloud Python API 以编程方式获得一个?
我看到一个 prior example using oauth2client, but oauth2client
is now deprecated. How would I do this with google.auth and oauthlib?
答案取决于您的环境以及您希望如何创建/获取凭据。
什么是 Google 云凭证?
Google 云凭据是一个 OAuth 2.0 令牌。此令牌至少有一个 Access Token
和可选的 Refresh Token
、Client ID Token
,以及支持参数,例如 expiration
、Service Account Email
或 Client Email
,等等
Google 云 API 中的重要项目是 Access Token
。此令牌用于授权访问云。此令牌可用于curl
等程序、python
等软件,不需要SDK。 Access Token
用于 HTTP Authorization
header.
什么是访问令牌?
访问令牌是由 Google 生成的不透明值,它派生自已签名的 JWT,更准确地说是 JWS。 JWT 由 header 和声明(有效负载)Json 结构组成。这两个 Json 结构是使用服务帐户的私钥签名的。这些值经过 base64 编码并连接起来以创建访问密钥。
访问令牌的格式为:base64(header) + '.' + base64(payload) + '.' + base64(signature)
.
这是一个 JWT 示例:
Header:
{
"alg": "RS256",
"typ": "JWT",
"kid": "42ba1e234ac91ffca687a5b5b3d0ca2d7ce0fc0a"
}
有效载荷:
{
"iss": "myservice@myproject.iam.gserviceaccount.com",
"iat": 1493833746,
"aud": "myservice.appspot.com",
"exp": 1493837346,
"sub": "myservice@myproject.iam.gserviceaccount.com"
}
使用访问令牌:
将启动 VM 实例的示例。替换 PROJECT_ID、ZONE 和 INSTANCE_NAME。此示例适用于 Windows.
curl -v -X GET -H "Authorization: Bearer <access_token_here>" ^
https://www.googleapis.com/compute/v1/projects/%PROJECT_ID%/zones/%ZONE%/instances/%INSTANCE_NAME%/start
Compute Engine 服务帐户:
对于这种情况,Dustin 的回答是正确的,但为了完整起见,我将包含一些其他信息。
这些凭据由 GCP 自动为您创建,并从 VM 实例元数据中获取。权限由 Google 控制台中的 Cloud API access scopes
控制。
但是,这些凭据有一些限制。要修改凭据,您必须先停止 VM 实例。此外,并非所有权限(角色)都受支持。
from google.auth import compute_engine
cred = compute_engine.Credentials()
服务帐户凭据:
在您了解所有类型的凭据及其用例之前,这些是您将用于除 gcloud
和 gsutil
之外的所有内容的凭据。了解这些凭据将使编写程序时使用 Google Cloud 变得更加简单。从 Google 服务帐户 Json 文件获取凭据很容易。唯一需要注意的是凭据过期(通常为 60 分钟)并且需要刷新或重新创建。
gcloud auth print-access-token
不推荐。服务帐户凭据是 Google 推荐的方法。
这些凭据由控制台、gcloud 或通过程序/API 创建。权限由 IAM 分配给信用凭证,并在 Compute Engine、App Engine、Firestore、Kubernetes 等内部以及 Google Cloud 之外的其他环境中运行。这些凭证是从 Google 云端下载并存储在 Json 文件中。注意 scopes
参数。这定义了授予生成的凭据的权限 object.
SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = 'service-account-credentials.json'
from google.oauth2 import service_account
cred = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
Google OAuth 2.0 凭据:
这些凭据源自完整的 OAuth 2.0 流程。这些凭据是在启动浏览器以访问 Google 帐户以授权访问时生成的。这个过程要复杂得多,需要大量代码才能实现,并且需要一个 built-in 网络服务器来进行授权回调。
此方法提供了额外的功能,例如能够 运行 浏览器中的所有内容,例如您可以创建云存储文件浏览器,但请注意了解安全隐患。此方法是用于支持 Google Sign-In 等的技术。我喜欢在允许在网站等上发帖之前使用此方法对用户进行身份验证。正确授权的 OAuth 2.0 身份和范围的可能性是无穷无尽的.
使用google_auth_oauthlib
的示例代码:
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=scope)
cred = flow.run_local_server(
host='localhost',
port=8088,
authorization_prompt_message='Please visit this URL: {url}',
success_message='The auth flow is complete; you may close this window.',
open_browser=True)
使用 requests_oauthlib
库的示例代码:
from requests_oauthlib import OAuth2Session
gcp = OAuth2Session(
app.config['gcp_client_id'],
scope=scope,
redirect_uri=redirect_uri)
# print('Requesting authorization url:', authorization_base_url)
authorization_url, state = gcp.authorization_url(
authorization_base_url,
access_type="offline",
prompt="consent",
include_granted_scopes='true')
session['oauth_state'] = state
return redirect(authorization_url)
# Next section of code after the browser approves the request
token = gcp.fetch_token(
token_url,
client_secret=app.config['gcp_client_secret'],
authorization_response=request.url)
虽然上面的答案提供了很多信息,但它遗漏了一个重点——从 google.auth.default()
或 compute_engine.Credentials()
获得的凭据对象将没有令牌。回到最初的问题,什么是 gcloud auth print-access-token
的编程替代方案,我的回答是:
import google.auth
import google.auth.transport.requests
creds, project = google.auth.default()
# creds.valid is False, and creds.token is None
# Need to refresh credentials to populate those
auth_req = google.auth.transport.requests.Request()
creds.refresh(auth_req)
# Now you can use creds.token
我正在使用官方 google-auth 包和 default credentials,这将让你在本地开发和远程 GCE/GKE 应用程序上运行。
太糟糕了,这没有正确记录,我不得不阅读 google-auth code 来弄清楚我们如何获得令牌。
我在寻找一种无需创建服务帐户即可使用 python SDK 的方法时发现了自己。我想要一种在本地开发脚本的方法,该脚本将 运行 在云中。我能够通过使用 gcloud 命令的工件来实现这一点:
export GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/legacy_credentials/<me>/adc.json
这可能不是推荐的方法,但对于我的应用程序中的 Rest API,这是获取令牌的简单方法。
from subprocess import PIPE, Popen
def cmdline(command):
process = Popen(
args=command,
stdout=PIPE,
shell=True
)
return process.communicate()[0]
token = cmdline("gcloud auth application-default print-access-token")
print("Token:"+token)
import google.auth
import google.auth.transport.requests
# getting the credentials and project details for gcp project
credentials, your_project_id = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
#getting request object
auth_req = google.auth.transport.requests.Request()
print(credentials.valid) # prints False
credentials.refresh(auth_req) #refresh token
#cehck for valid credentials
print(credentials.valid) # prints True
print(credentials.token) # prints token
合并来自此 post 和 google 云文档的建议,我编写了一个辅助函数,returns 一个令牌。如果可能,它会生成一个令牌,如果不能,则从环境中获取它,然后检查它是否有效。
import google
import os
import requests
GOOGLE_APPLICATION_CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS"
GCS_OAUTH_TOKEN = "GCS_OAUTH_TOKEN"
SCOPE = "https://www.googleapis.com/auth/cloud-platform"
URL = "https://www.googleapis.com/oauth2/v1/tokeninfo"
PAYLOAD = "access_token={}"
HEADERS = {"content-type": "application/x-www-form-urlencoded"}
OK = "OK"
def get_gcs_token():
"""
Returns gcs access token.
Ideally, this function generates a new token, requries that GOOGLE_APPLICATION_CREDENTIALS be set in the environment
(os.environ).
Alternatively, environment variable GCS_OAUTH_TOKEN could be set if a token already exists
"""
if GOOGLE_APPLICATION_CREDENTIALS in os.environ:
# getting the credentials and project details for gcp project
credentials, your_project_id = google.auth.default(scopes=[SCOPE])
# getting request object
auth_req = google.auth.transport.requests.Request()
credentials.refresh(auth_req) # refresh token
token = credentials.token
elif GCS_OAUTH_TOKEN in os.environ:
token = os.environ[GCS_OAUTH_TOKEN]
else:
raise ValueError(
f"""Could not generate gcs token because {GOOGLE_APPLICATION_CREDENTIALS} is not set in the environment.
Alternatively, environment variable {GCS_OAUTH_TOKEN} could be set if a token already exists, but it was not"""
)
r = requests.post(URL, data=PAYLOAD.format(token), headers=HEADERS)
if not r.reason == OK:
raise ValueError(
f"Could not verify token {token}\n\nResponse from server:\n{r.text}"
)
if not r.json()["expires_in"] > 0:
raise ValueError(f"token {token} expired")
return token
在某些情况下,无法在服务器或容器上设置环境变量,同时需要 Bearer 访问令牌来调用 Google 云 APIs。我提出以下方法来解决这个问题:
# pip3 install google-auth
# pip3 install requests
import google.auth
import google.auth.transport.requests
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file('/home/user/secrets/hil-test.json', scopes=['https://www.googleapis.com/auth/cloud-platform'])
auth_req = google.auth.transport.requests.Request()
credentials.refresh(auth_req)
credentials.token
最后一行将打印调用 Google 云 API 的访问令牌。将以下 curl 命令中的 ya29<REDACTED>
替换为来自 python 的打印标记作为测试:
curl https://example.googleapis.com/v1alpha1/projects/PROJECT_ID/locations -H "Authorization: Bearer ya29<REDACTED>"
执行 python 获取令牌然后卷入 BASH 调用 API 可能没有意义。目的是演示获取令牌以调用 Google 云 Alpha API,它可能没有任何 Python 客户端库,但有 REST API。然后开发人员可以使用 Python requests HTTP 库来调用 APIs.
gcloud auth print-access-token
给了我一个我以后可以使用的 Bearer 令牌;然而,这是一个 shell 命令。我如何通过 Google Cloud Python API 以编程方式获得一个?
我看到一个 prior example using oauth2client, but oauth2client
is now deprecated. How would I do this with google.auth and oauthlib?
答案取决于您的环境以及您希望如何创建/获取凭据。
什么是 Google 云凭证?
Google 云凭据是一个 OAuth 2.0 令牌。此令牌至少有一个 Access Token
和可选的 Refresh Token
、Client ID Token
,以及支持参数,例如 expiration
、Service Account Email
或 Client Email
,等等
Google 云 API 中的重要项目是 Access Token
。此令牌用于授权访问云。此令牌可用于curl
等程序、python
等软件,不需要SDK。 Access Token
用于 HTTP Authorization
header.
什么是访问令牌?
访问令牌是由 Google 生成的不透明值,它派生自已签名的 JWT,更准确地说是 JWS。 JWT 由 header 和声明(有效负载)Json 结构组成。这两个 Json 结构是使用服务帐户的私钥签名的。这些值经过 base64 编码并连接起来以创建访问密钥。
访问令牌的格式为:base64(header) + '.' + base64(payload) + '.' + base64(signature)
.
这是一个 JWT 示例:
Header:
{
"alg": "RS256",
"typ": "JWT",
"kid": "42ba1e234ac91ffca687a5b5b3d0ca2d7ce0fc0a"
}
有效载荷:
{
"iss": "myservice@myproject.iam.gserviceaccount.com",
"iat": 1493833746,
"aud": "myservice.appspot.com",
"exp": 1493837346,
"sub": "myservice@myproject.iam.gserviceaccount.com"
}
使用访问令牌:
将启动 VM 实例的示例。替换 PROJECT_ID、ZONE 和 INSTANCE_NAME。此示例适用于 Windows.
curl -v -X GET -H "Authorization: Bearer <access_token_here>" ^
https://www.googleapis.com/compute/v1/projects/%PROJECT_ID%/zones/%ZONE%/instances/%INSTANCE_NAME%/start
Compute Engine 服务帐户:
对于这种情况,Dustin 的回答是正确的,但为了完整起见,我将包含一些其他信息。
这些凭据由 GCP 自动为您创建,并从 VM 实例元数据中获取。权限由 Google 控制台中的 Cloud API access scopes
控制。
但是,这些凭据有一些限制。要修改凭据,您必须先停止 VM 实例。此外,并非所有权限(角色)都受支持。
from google.auth import compute_engine
cred = compute_engine.Credentials()
服务帐户凭据:
在您了解所有类型的凭据及其用例之前,这些是您将用于除 gcloud
和 gsutil
之外的所有内容的凭据。了解这些凭据将使编写程序时使用 Google Cloud 变得更加简单。从 Google 服务帐户 Json 文件获取凭据很容易。唯一需要注意的是凭据过期(通常为 60 分钟)并且需要刷新或重新创建。
gcloud auth print-access-token
不推荐。服务帐户凭据是 Google 推荐的方法。
这些凭据由控制台、gcloud 或通过程序/API 创建。权限由 IAM 分配给信用凭证,并在 Compute Engine、App Engine、Firestore、Kubernetes 等内部以及 Google Cloud 之外的其他环境中运行。这些凭证是从 Google 云端下载并存储在 Json 文件中。注意 scopes
参数。这定义了授予生成的凭据的权限 object.
SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = 'service-account-credentials.json'
from google.oauth2 import service_account
cred = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
Google OAuth 2.0 凭据:
这些凭据源自完整的 OAuth 2.0 流程。这些凭据是在启动浏览器以访问 Google 帐户以授权访问时生成的。这个过程要复杂得多,需要大量代码才能实现,并且需要一个 built-in 网络服务器来进行授权回调。
此方法提供了额外的功能,例如能够 运行 浏览器中的所有内容,例如您可以创建云存储文件浏览器,但请注意了解安全隐患。此方法是用于支持 Google Sign-In 等的技术。我喜欢在允许在网站等上发帖之前使用此方法对用户进行身份验证。正确授权的 OAuth 2.0 身份和范围的可能性是无穷无尽的.
使用google_auth_oauthlib
的示例代码:
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=scope)
cred = flow.run_local_server(
host='localhost',
port=8088,
authorization_prompt_message='Please visit this URL: {url}',
success_message='The auth flow is complete; you may close this window.',
open_browser=True)
使用 requests_oauthlib
库的示例代码:
from requests_oauthlib import OAuth2Session
gcp = OAuth2Session(
app.config['gcp_client_id'],
scope=scope,
redirect_uri=redirect_uri)
# print('Requesting authorization url:', authorization_base_url)
authorization_url, state = gcp.authorization_url(
authorization_base_url,
access_type="offline",
prompt="consent",
include_granted_scopes='true')
session['oauth_state'] = state
return redirect(authorization_url)
# Next section of code after the browser approves the request
token = gcp.fetch_token(
token_url,
client_secret=app.config['gcp_client_secret'],
authorization_response=request.url)
虽然上面的答案提供了很多信息,但它遗漏了一个重点——从 google.auth.default()
或 compute_engine.Credentials()
获得的凭据对象将没有令牌。回到最初的问题,什么是 gcloud auth print-access-token
的编程替代方案,我的回答是:
import google.auth
import google.auth.transport.requests
creds, project = google.auth.default()
# creds.valid is False, and creds.token is None
# Need to refresh credentials to populate those
auth_req = google.auth.transport.requests.Request()
creds.refresh(auth_req)
# Now you can use creds.token
我正在使用官方 google-auth 包和 default credentials,这将让你在本地开发和远程 GCE/GKE 应用程序上运行。
太糟糕了,这没有正确记录,我不得不阅读 google-auth code 来弄清楚我们如何获得令牌。
我在寻找一种无需创建服务帐户即可使用 python SDK 的方法时发现了自己。我想要一种在本地开发脚本的方法,该脚本将 运行 在云中。我能够通过使用 gcloud 命令的工件来实现这一点:
export GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/legacy_credentials/<me>/adc.json
这可能不是推荐的方法,但对于我的应用程序中的 Rest API,这是获取令牌的简单方法。
from subprocess import PIPE, Popen
def cmdline(command):
process = Popen(
args=command,
stdout=PIPE,
shell=True
)
return process.communicate()[0]
token = cmdline("gcloud auth application-default print-access-token")
print("Token:"+token)
import google.auth
import google.auth.transport.requests
# getting the credentials and project details for gcp project
credentials, your_project_id = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
#getting request object
auth_req = google.auth.transport.requests.Request()
print(credentials.valid) # prints False
credentials.refresh(auth_req) #refresh token
#cehck for valid credentials
print(credentials.valid) # prints True
print(credentials.token) # prints token
合并来自此 post 和 google 云文档的建议,我编写了一个辅助函数,returns 一个令牌。如果可能,它会生成一个令牌,如果不能,则从环境中获取它,然后检查它是否有效。
import google
import os
import requests
GOOGLE_APPLICATION_CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS"
GCS_OAUTH_TOKEN = "GCS_OAUTH_TOKEN"
SCOPE = "https://www.googleapis.com/auth/cloud-platform"
URL = "https://www.googleapis.com/oauth2/v1/tokeninfo"
PAYLOAD = "access_token={}"
HEADERS = {"content-type": "application/x-www-form-urlencoded"}
OK = "OK"
def get_gcs_token():
"""
Returns gcs access token.
Ideally, this function generates a new token, requries that GOOGLE_APPLICATION_CREDENTIALS be set in the environment
(os.environ).
Alternatively, environment variable GCS_OAUTH_TOKEN could be set if a token already exists
"""
if GOOGLE_APPLICATION_CREDENTIALS in os.environ:
# getting the credentials and project details for gcp project
credentials, your_project_id = google.auth.default(scopes=[SCOPE])
# getting request object
auth_req = google.auth.transport.requests.Request()
credentials.refresh(auth_req) # refresh token
token = credentials.token
elif GCS_OAUTH_TOKEN in os.environ:
token = os.environ[GCS_OAUTH_TOKEN]
else:
raise ValueError(
f"""Could not generate gcs token because {GOOGLE_APPLICATION_CREDENTIALS} is not set in the environment.
Alternatively, environment variable {GCS_OAUTH_TOKEN} could be set if a token already exists, but it was not"""
)
r = requests.post(URL, data=PAYLOAD.format(token), headers=HEADERS)
if not r.reason == OK:
raise ValueError(
f"Could not verify token {token}\n\nResponse from server:\n{r.text}"
)
if not r.json()["expires_in"] > 0:
raise ValueError(f"token {token} expired")
return token
在某些情况下,无法在服务器或容器上设置环境变量,同时需要 Bearer 访问令牌来调用 Google 云 APIs。我提出以下方法来解决这个问题:
# pip3 install google-auth
# pip3 install requests
import google.auth
import google.auth.transport.requests
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file('/home/user/secrets/hil-test.json', scopes=['https://www.googleapis.com/auth/cloud-platform'])
auth_req = google.auth.transport.requests.Request()
credentials.refresh(auth_req)
credentials.token
最后一行将打印调用 Google 云 API 的访问令牌。将以下 curl 命令中的 ya29<REDACTED>
替换为来自 python 的打印标记作为测试:
curl https://example.googleapis.com/v1alpha1/projects/PROJECT_ID/locations -H "Authorization: Bearer ya29<REDACTED>"
执行 python 获取令牌然后卷入 BASH 调用 API 可能没有意义。目的是演示获取令牌以调用 Google 云 Alpha API,它可能没有任何 Python 客户端库,但有 REST API。然后开发人员可以使用 Python requests HTTP 库来调用 APIs.