【问题标题】:Use oauth2 service account to authenticate to Google API in python在 python 中使用 oauth2 服务帐户向 Google API 进行身份验证
【发布时间】:2014-08-21 18:29:36
【问题描述】:

我已按照 https://developers.google.com/accounts/docs/OAuth2ServiceAccount 中的说明使用服务帐户对 Google Cloud Storage API 进行身份验证。我尝试在 python 中向谷歌的身份验证服务器发送 JWT,但出现错误:

urllib2.HTTPError: HTTP Error 400: Bad Request

我制作、签名或发送 JWT 的方式似乎有问题?该错误不是特定的,因此它可能是该过程的任何部分。有人有什么想法吗?

import Crypto.PublicKey.RSA as RSA
import Crypto.Hash.SHA as SHA
import Crypto.Signature.PKCS1_v1_5 as PKCS1_v1_5
import base64
import json
import time
import urllib2
import urllib

# Settings
json_key_file = 'GooglePM-9f75ad112f87-service.json'

# Load the private key associated with the Google service account
with open(json_key_file) as json_file:
    json_data = json.load(json_file)
    key = RSA.importKey(json_data['private_key'])

# Create an PKCS1_v1_5 object
signer = PKCS1_v1_5.new(key)

# Encode the JWT header
header_b64 = base64.urlsafe_b64encode(json.dumps({'alg':'RS256','typ':'JWT'}))

# JWT claims
jwt = {
    'iss': json_data['client_email'],
    'scope': 'https://www.googleapis.com/auth/devstorage.read_write',
    'aud': 'https://accounts.google.com/o/oauth2/token',
    'exp': int(time.time())+3600,
    'iat': int(time.time())
    }
jwt_json = json.dumps(jwt)

# Encode the JWT claims
jwt_json_b64 = base64.urlsafe_b64encode(jwt_json)

# Sign the JWT header and claims
msg_hash = SHA.new(header_b64 + "." + jwt_json_b64)
signature_b64 = base64.urlsafe_b64encode(signer.sign(msg_hash))

# Make the complete message
jwt_complete = header_b64 + "." + jwt_json_b64 + "." + signature_b64

data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 
    'assertion': jwt_complete}

f = urllib2.urlopen("https://accounts.google.com/o/oauth2/token", urllib.urlencode(data))

print f.read()

如果我尝试使用 curl 发布到服务器,我会收到无效授权错误:

(venv)$ curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiMTM1MDY3NjIyMTk4LWVhbWUwZnFqdTNvamRoZ29zdDg2dnBpdTBsYW91NnZlQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwgInNjb3BlIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIiwgImF1ZCI6ICJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvdG9rZW4iLCAiZXhwIjogMTQwODY1MTU2OCwgImlhdCI6IDE0MDg2NDg1NTh9.HWC7h3QiOy7QsSuta4leq_Gjwmy9IdF-MUwflPhiohzAJ-Amykd56Ye4Y_Saf_sAc5STzOCmrSPzOTYvGXr6X_T_AmSTxXK2AJ2SpAiEUs2_Wp5h18xTUY3Y_hkKvSZLh5bRzeJ_0xRcmRIPE6tua0FHFwUDdnCIGdh4DGg6i4E%3D' https://accounts.google.com/o/oauth2/token
{
  "error" : "invalid_grant"
}

【问题讨论】:

    标签: python google-cloud-storage google-compute-engine


    【解决方案1】:

    好的,所以有更好的方法来做到这一点!谷歌已经有一个 python 客户端 API 来处理一些复杂性。以下代码在安装 google python 客户端 API 后有效:https://developers.google.com/api-client-library/python/guide/aaa_oauth

    from oauth2client.client import SignedJwtAssertionCredentials
    import json
    import urllib
    import urllib2
    
    # Settings
    json_key_file = 'GooglePM-9f75ad112f87-service.json'
    
    # Load the private key associated with the Google service account
    with open(json_key_file) as json_file:
        json_data = json.load(json_file)
    
    # Get and sign JWT
    credential = SignedJwtAssertionCredentials(json_data['client_email'], json_data['private_key'], 'https://www.googleapis.com/auth/devstorage.read_write')
    jwt_complete = credential._generate_assertion()
    
    # Get token from server
    data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 
        'assertion': jwt_complete}
    f = urllib2.urlopen("https://accounts.google.com/o/oauth2/token", urllib.urlencode(data))
    
    print f.read()
    

    【讨论】:

    • 非常好的信息,谢谢。在 Python 3 中,json_data['private_key'] 变为 bytes(json_data['private_key'], 'UTF-8')
    【解决方案2】:

    也许,稍微简单一点:

    import oauth2client.service_account
    
    jsonfile = 'GooglePM-9f7sdf342f87-service.json'
    # use static method .from_json_keyfile_name(filename)
    credentials = oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name(jsonfile)
    

    【讨论】:

      【解决方案3】:

      您的第一个算法完全可以正常工作,但您需要 SHA256,而不是 SHA。谢谢你的代码。

      【讨论】:

        【解决方案4】:

        初始代码也可以使用 Python 3.6。在 base64 的情况下,您只需使用 bytes 而不是 str:

        import requests
        import json as js
        import Crypto.PublicKey.RSA as RSA
        import Crypto.Hash.SHA256 as SHA
        import Crypto.Signature.PKCS1_v1_5 as PKCS1_v1_5
        import base64
        import time
        
        # Settings
        json_key_file = 'google-api.json'
        
        # Load the private key associated with the Google service account
        with open(json_key_file) as json_file:
            json_data = js.load(json_file)
            key = RSA.importKey(json_data['private_key'])
        
        # Create an PKCS1_v1_5 object
        signer = PKCS1_v1_5.new(key)
        
        header = js.dumps({'alg':'RS256','typ':'JWT'})
        
        # Encode the JWT header
        header_b64 = base64.urlsafe_b64encode(header.encode("UTF-8"))
        
        # JWT claims
        jwt = {
            'iss': json_data['client_email'],
            'scope': 'https://www.googleapis.com/auth/analytics.readonly',
            'aud': 'https://accounts.google.com/o/oauth2/token',
            'exp': int(time.time())+3600,
            'iat': int(time.time())
            }
        jwt_json = js.dumps(jwt)
        
        # Encode the JWT claims
        jwt_json_b64 = base64.urlsafe_b64encode(jwt_json.encode("UTF-8"))
        
        # Sign the JWT header and claims
        msg_hash = SHA.new((header_b64.decode("UTF-8") + "." + jwt_json_b64.decode("UTF-8")).encode("UTF-8"))
        signature_b64 = base64.urlsafe_b64encode(signer.sign(msg_hash))
        
        # Make the complete message
        jwt_complete = (header_b64.decode("UTF-8") + "." + jwt_json_b64.decode("UTF-8") + "." + signature_b64.decode("UTF-8")).encode("UTF-8")
        
        data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 
        'assertion': jwt_complete}
        
        requests.post(url="https://accounts.google.com/o/oauth2/token",data=data).text
        

        【讨论】:

          猜你喜欢
          • 2013-09-23
          • 1970-01-01
          • 2020-06-24
          • 2015-09-16
          • 2017-11-03
          • 1970-01-01
          • 1970-01-01
          • 2020-05-21
          • 2019-08-25
          相关资源
          最近更新 更多