我的目标是不依赖于gcloud二进制文件而复制/复制gcloud compute addresses create
的功能。
我正在根据https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address上有关保留静态外部IP地址的文档,尝试使用python对googleapis计算端点进行POST身份验证
但是我的POST每次都会返回401。
我已经从google.auth.jwt python模块创建了一个JWT,当我对其进行解码时,JWT嵌入了所有我希望放在其中的字符串。
我还尝试将以下OAuth范围的组合包含在JWT中: -“ https://www.googleapis.com/auth/userinfo.email” -“ https://www.googleapis.com/auth/compute” -“ https://www.googleapis.com/auth/cloud-platform”
这是我使用服务帐户的JSON密钥文件中的信息获取JWT的功能
def _generate_jwt( tokenPath, expiry_length=3600 ):
now = int(time.time())
tokenData = load_json_data( tokenPath )
sa_email = tokenData['client_email']
payload = {
'iat': now,
# expires after 'expiry_length' seconds.
"exp": now + expiry_length,
'iss': sa_email,
"scope": " ".join( [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/userinfo.email"
] ),
'aud': "https://www.googleapis.com/oauth2/v4/token",
'email': sa_email
}
# sign with keyfile
signer = google.auth.crypt.RSASigner.from_service_account_file( tokenPath )
jwt = google.auth.jwt.encode(signer, payload)
return jwt
一旦我有了JWT,然后发布以下失败的信息401,::
gapiURL = 'https://www.googleapis.com/compute/v1/projects/' + projectID + '/regions/' + region + '/addresses'
jwtToken = _generate_jwt( servicetoken )
headers = {
'Authorization': 'Bearer {}'.format( jwtToken ),
'content-type' : 'application/json',
}
post = requests.post( url=gapiURL, headers=headers, data=data )
post.raise_for_status()
return post.text
无论我在JWT中使用的范围或向服务帐户提供的权限的多少组合,我都收到401。我在做什么错了?
编辑:非常感谢@JohnHanley指出我错过了GCP身份验证序列中https://www.googleapis.com/oauth2/v4/token URL的下一个/第二个POST。因此,您将获得一个JWT以获取一个“访问令牌”。
我已将调用更改为使用python jwt模块,而不是将google.auth.jwt模块与google.auth.crypt.RSASigner组合使用。因此,代码要简单一些,我将其放在一个方法中
## serviceAccount auth sequence for google :: JWT -> accessToken
def gke_get_token( serviceKeyDict, expiry_seconds=3600 ):
epoch_time = int(time.time())
# Generate a claim from the service account file.
claim = {
"iss": serviceKeyDict["client_email"],
"scope": " ".join([
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/userinfo.email"
]),
"aud": "https://www.googleapis.com/oauth2/v4/token",
"exp": epoch_time + expiry_seconds,
"iat": epoch_time
}
# Sign claim with JWT.
assertion = jwt.encode( claim, serviceKeyDict["private_key"], algorithm='RS256' ).decode()
data = urllib.urlencode( {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": assertion
} )
# Request the access token.
result = requests.post(
url="https://www.googleapis.com/oauth2/v4/token",
headers={
"Content-Type": "application/x-www-form-urlencoded"
},
data=data
)
result.raise_for_status()
return loadJsonData(result.text)["access_token"]
答案 0 :(得分:2)
在Google Cloud中,可以授予访问权限的三种“令牌”:
在您的情况下,您创建了一个签名JWT。一些Google服务接受此令牌。大多数人没有。
创建签名的JWT后,下一步就是调用Google OAuth端点并交换访问令牌。我写了一篇文章对此进行了详细描述:
Google Cloud – Creating OAuth Access Tokens for REST API Calls
某些Google服务现在接受身份令牌。这称为基于身份的访问控制(IBAC)。这不适用于您的问题,但这是Google Cloud Authorization未来的趋势。一个例子是我关于Cloud Run + Cloud Storage + KMS的文章:
Google Cloud – Go – Identity Based Access Control
以下示例Python代码显示了如何交换令牌:
def exchangeJwtForAccessToken(signed_jwt):
'''
This function takes a Signed JWT and exchanges it for a Google OAuth Access Token
'''
auth_url = "https://www.googleapis.com/oauth2/v4/token"
params = {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": signed_jwt
}
r = requests.post(auth_url, data=params)
if r.ok:
return(r.json()['access_token'], '')
return None, r.text