【问题标题】:How to get a GCP Bearer token programmatically with python如何使用 python 以编程方式获取 GCP Bearer 令牌
【发布时间】:2019-04-27 14:03:16
【问题描述】:

gcloud auth print-access-token 给了我一个 Bearer 令牌,我以后可以使用它;但是,这是一个 shell 命令。我如何通过 Google Cloud Python API 以编程方式获取一个?

我看到 prior example 使用 oauth2client,但 oauth2client 现在已弃用。我将如何使用 google.authoauthlib 做到这一点?

【问题讨论】:

  • 您想在 Compute Engine、App Engine、Cloud Functions 等 GCP 环境中获得此功能吗?还是在 GCP 之外,例如在您的本地计算机上?
  • 我正在使用服务帐户在 GCP Compute Engine 实例中构建应用/脚本。这是我的第一选择。我也很想知道如果它也在我的本地机器上的 GCP 之外运行会是什么样子。

标签: python google-cloud-platform google-iam


【解决方案1】:

答案取决于您的环境以及您希望如何创建/获取凭据。

什么是 Google Cloud 凭据?

Google Cloud 凭据是 OAuth 2.0 令牌。这个令牌至少有一个Access Token 和可选的Refresh TokenClient ID Token,以及诸如expirationService Account EmailClient Email 等支持参数。

Google Cloud API 中的重要项是Access Token。这个令牌是授权访问云的东西。该令牌可用于curl等程序、python等软件等,不需要SDK。 Access Token 用于 HTTP Authorization 标头中。

什么是访问令牌?

访问令牌是由 Google 生成的不透明值,它派生自签名的 JWT,更准确地说是 JWS。 JWT 由标头和声明(有效负载)Json 结构组成。这两个 Json 结构使用服务帐户的私钥进行签名。这些值经过 base64 编码和连接以创建访问密钥。

Access Token 的格式为:base64(header) + '.' + base64(payload) + '.' + base64(signature)

这是一个 JWT 示例:

标题:

{
  "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 自动为您创建,并从虚拟机实例元数据中获取。权限由 Google 控制台中的Cloud API access scopes 控制。

但是,这些凭据有一些限制。要修改凭据,您必须先停止 VM 实例。此外,并非所有权限(角色)都受支持。

from google.auth import compute_engine

cred = compute_engine.Credentials()

服务帐户凭据:

在您了解所有类型的凭据及其用例之前,这些凭据将用于除 gcloudgsutil 之外的所有内容。了解这些凭据将使编写程序时使用 Google Cloud 变得更加简单。从 Google 服务帐户 Json 文件中获取凭据很容易。唯一需要注意的是凭据过期(通常为 60 分钟)并且需要刷新或重新创建。

gcloud auth print-access-token 不推荐。服务帐户凭据是 Google 推荐的方法。

这些凭据由控制台、gcloud 或通过程序/API 创建。权限由 IAM 分配给贷方,并在 Compute Engine、App Engine、Firestore、Kubernetes 等以及 Google Cloud 之外的其他环境中发挥作用。这些凭据从 Google Cloud 下载并存储在 Json 文件中。注意scopes 参数。这定义了授予结果凭证对象的权限。

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 帐户以授权访问时生成的。这个过程要复杂得多,需要大量的代码来实现,并且需要一个内置的 Web 服务器来进行授权回调。

此方法提供了附加功能,例如能够在浏览器中运行所有内容,例如您可以创建云存储文件浏览器,但请注意了解安全隐患。此方法是用于支持 Google 登录等的技术。我喜欢在允许在网站等上发布之前使用此方法对用户进行身份验证。正确授权的 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)

【讨论】:

  • 我还看到了这个 github 示例:github.com/GoogleCloudPlatform/python-docs-samples/blob/master/…这与您上面提到的讨论有什么关系?
  • 此示例从 VM 实例元数据中获取访问令牌。这相当于我在 Compute Engine cred = compute_engine.Credentials() 下的答案中的代码。这个例子展示了如何直接访问元数据服务器,这对了解/理解也很重要。
  • 如果您有兴趣进一步了解 Google Cloud Credentials,我在我的网站上写了一堆文章:jhanley.com
  • 再次感谢您的解释和进一步阅读的链接!
【解决方案2】:

虽然上述答案提供了很多信息,但它忽略了一个重要点 - 从 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 以了解我们如何获取令牌。

【讨论】:

  • 这对我有用,虽然我需要添加谷歌云范围只是为了进行刷新。所以我将导入后的第一行更改为:“creds, project = google.auth.default(scopes=['googleapis.com/auth/cloud-platform'])”
  • 谢谢你,我走的是同一条路,你为我节省了很多时间
  • 这最终会使用用户的凭据,但是有没有办法使用 appengine 服务帐户的凭据?
  • 它将获得应用程序默认凭据 - 如果您在笔记本电脑上运行,这些通常是您的用户凭据。在 GAE 中,它将是它的服务帐户凭据等。blog.doit-intl.com/…
  • 这正是我要找的东西!!!你救了我的夜晚@ZaarHai
【解决方案3】:

我在寻找一种无需创建服务帐户即可使用 python SDK 的方法时发现自己在这里。我想要一种在本地开发可以在云中运行的脚本的方法。我能够通过使用 gcloud 命令的工件来实现这一点:

export GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/legacy_credentials/<me>/adc.json

【讨论】:

    【解决方案4】:

    这可能不是推荐的方法,但对于我的应用程序中的 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)
    

    【讨论】:

    • 这可以在大多数应用程序中使用,但是当我们尝试使用 Cloud Functions 部署触发器时它不起作用。
    【解决方案5】:
    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
    

    【讨论】:

    • 谢谢!这是最好的答案! ?
    【解决方案6】:

    结合这篇文章和谷歌云文档的建议,我编写了一个返回令牌的辅助函数。如果可能,它会生成一个令牌,如果没有,它会从环境中获取它,然后检查它是否有效。

    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
    

    【讨论】:

      【解决方案7】:

      在某些情况下,无法在服务器或容器上设置环境变量,同时需要不记名访问令牌来调用 Google 云 API。我提出以下内容来解决此类问题:

      # 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&lt;REDACTED&gt; 替换为来自 python 的打印令牌作为测试:

      curl https://example.googleapis.com/v1alpha1/projects/PROJECT_ID/locations -H "Authorization: Bearer ya29<REDACTED>"
      

      执行 python 获取令牌然后在 BASH 中 curl 调用 API 可能没有意义。目的是演示获取令牌以调用可能没有任何 Python 客户端库但 REST API 的谷歌云 Alpha API。然后,开发者可以使用Python requests HTTP 库来调用 API。

      【讨论】:

      • 兄弟使用服务帐户json文件获取令牌的完美答案....
      猜你喜欢
      • 1970-01-01
      • 2021-05-10
      • 2019-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 2019-09-18
      相关资源
      最近更新 更多