Google cloud platform 带有JWT令牌的Google云存储JSON API

Google cloud platform 带有JWT令牌的Google云存储JSON API,google-cloud-platform,google-cloud-storage,Google Cloud Platform,Google Cloud Storage,我正在尝试使用谷歌云存储的JSON API从谷歌云存储中检索文件。我不允许使用SDK。是否可以从ServiceAccount.json文件创建JWT并使用JWT从Google云存储访问文件?我在node.js中有一个脚本,可以从服务帐户生成JWT,但我不确定受众是否正确 const jwt = require('jsonwebtoken'); const serviceAccount = require('./serviceAccount.json'); const issuedAt = Mat

我正在尝试使用谷歌云存储的JSON API从谷歌云存储中检索文件。我不允许使用SDK。是否可以从ServiceAccount.json文件创建JWT并使用JWT从Google云存储访问文件?我在node.js中有一个脚本,可以从服务帐户生成JWT,但我不确定受众是否正确

const jwt = require('jsonwebtoken');
const serviceAccount = require('./serviceAccount.json');
const issuedAt = Math.floor(Date.now() / 1000);

const TOKEN_DURATION_IN_SECONDS = 3600;

let params = {
    'iss': serviceAccount.client_email,
    'sub': serviceAccount.client_email,
    'aud': serviceAccount.project_id,
    'iat': issuedAt,
    'exp': issuedAt + TOKEN_DURATION_IN_SECONDS,
};

let options = {
    algorithm: 'RS256',
    header: {
        'kid': serviceAccount.private_key_id,
        'typ': 'JWT',
        'alg': 'RS256',
    },
};

let token = jwt.sign(params, serviceAccount.private_key, options);
console.log(token);
然后,我使用该JWT调用Google云存储JSON API:

https://www.googleapis.com/storage/v1/b/test

使用标头:
授权承载{token}

这只会导致
无效凭据的响应

有几个问题:

  • 我不确定创建JWT时“aud”应该是什么。我见过一些例子,其中它是一个url,也有一个projectId。两个都不适合我
  • 其中一个JSONAPI示例说授权令牌应该是oauth令牌。我可以改用JWT吗?还是需要使用JWT打电话来获取访问令牌
  • 我的铲斗路径正确吗?bucket路径的基本文件夹是您的projectId吗?我的路径应该是
    /{projectId}/test
    。我两个都试过,两个都没试过
重述

这是一个物联网项目,我需要嵌入式设备从谷歌云存储下载文件。我需要创建一个web门户,将文件上载到(使用Firebase函数)并向设备传递一个bucket路径或一个私有/签名URL。底线是我需要使用服务帐户密钥访问Google云存储桶。如果有一个嵌入式SDK-很好,但是我找不到一个用于C的。我唯一的想法是使用JSON API。如果有一种方法,我可以签署一个只能使用服务帐户访问的URL-这也是可行的


谢谢

是的,您可以从服务帐户Json(或P12)文件创建自己的签名JWT,并将JWT交换为访问令牌,然后将其用作
授权:承载令牌

我已经写了很多关于如何使用Json和P12凭据的文章

关于您的问题:

我不确定创建JWT时“aud”应该是什么。我见过 例如,它是url,也是projectId。也不 为我工作

aud
设置为
”https://www.googleapis.com/oauth2/v4/token“

其中一个JSONAPI示例说授权令牌应该是 oauth令牌。我可以改用JWT吗?还是需要用JWT打电话 JWT是否要获取访问令牌

一些API接受签名的JWT,其他API则期望OAuth访问令牌。总是获得OAuth访问令牌更容易。在我下面的示例代码中,我向您展示了如何

我的铲斗路径正确吗?是存储桶路径的基本文件夹吗 投射物?我的路径应该是/{projectId}/test。我试过两种方法 两者都不起作用

您的url应该如下所示(Python字符串构建示例)

下面我将向您展示如何呼叫两个服务(GCE和GCS)。大多数GoogleAPI将遵循类似的样式来构建RESTAPI URL

从问题中的代码来看,您缺少OAuth过程中的最后一步。您需要将已签名的JWT交换为访问令牌

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
下面是一个完整的Python3.x示例,它将列出GCE实例。此代码下面是显示GCS存储桶的更改

'''
This program lists lists the Google Compute Engine Instances in one zone
'''

import time
import json
import jwt
import requests
import httplib2

# Project ID for this request.
project = 'development-123456'

# The name of the zone for this request.
zone = 'us-west1-a'

# Service Account Credentials, Json format
json_filename = 'service-account.json'

# Permissions to request for Access Token
scopes = "https://www.googleapis.com/auth/cloud-platform"

# Set how long this token will be valid in seconds
expires_in = 3600   # Expires in 1 hour

def load_json_credentials(filename):
    ''' Load the Google Service Account Credentials from Json file '''

    with open(filename, 'r') as f:
        data = f.read()

    return json.loads(data)

def load_private_key(json_cred):
    ''' Return the private key from the json credentials '''

    return json_cred['private_key']

def create_signed_jwt(pkey, pkey_id, email, scope):
    '''
    Create a Signed JWT from a service account Json credentials file
    This Signed JWT will later be exchanged for an Access Token
    '''

    # Google Endpoint for creating OAuth 2.0 Access Tokens from Signed-JWT
    auth_url = "https://www.googleapis.com/oauth2/v4/token"

    issued = int(time.time())
    expires = issued + expires_in   # expires_in is in seconds

    # Note: this token expires and cannot be refreshed. The token must be recreated

    # JWT Headers
    additional_headers = {
            'kid': pkey_id,
            "alg": "RS256",
            "typ": "JWT"    # Google uses SHA256withRSA
    }

    # JWT Payload
    payload = {
        "iss": email,       # Issuer claim
        "sub": email,       # Issuer claim
        "aud": auth_url,    # Audience claim
        "iat": issued,      # Issued At claim
        "exp": expires,     # Expire time
        "scope": scope      # Permissions
    }

    # Encode the headers and payload and sign creating a Signed JWT (JWS)
    sig = jwt.encode(payload, pkey, algorithm="RS256", headers=additional_headers)

    return sig

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

def gce_list_instances(accessToken):
    '''
    This functions lists the Google Compute Engine Instances in one zone
    '''

    # Endpoint that we will call
    url = "https://www.googleapis.com/compute/v1/projects/" + project + "/zones/" + zone + "/instances"

    # One of the headers is "Authorization: Bearer $TOKEN"
    headers = {
        "Host": "www.googleapis.com",
        "Authorization": "Bearer " + accessToken,
        "Content-Type": "application/json"
    }

    h = httplib2.Http()

    resp, content = h.request(uri=url, method="GET", headers=headers)

    status = int(resp.status)

    if status < 200 or status >= 300:
        print('Error: HTTP Request failed')
        return

    j = json.loads(content.decode('utf-8').replace('\n', ''))

    print('Compute instances in zone', zone)
    print('------------------------------------------------------------')
    for item in j['items']:
        print(item['name'])

if __name__ == '__main__':
    cred = load_json_credentials(json_filename)

    private_key = load_private_key(cred)

    s_jwt = create_signed_jwt(
            private_key,
            cred['private_key_id'],
            cred['client_email'],
            scopes)

    token, err = exchangeJwtForAccessToken(s_jwt)

    if token is None:
        print('Error:', err)
        exit(1)

    gce_list_instances(token)

我发现这个[没有OAuth的服务帐户授权](

您可以避免在进行API调用之前向Google的授权服务器发出网络请求

中列出了可用的API。 根据存储库中的评论,谷歌云存储api似乎尚未发布


您是否能够将云存储API与JWT一起使用?

您是否绝对确定您需要使用特定的服务帐户?在云功能中运行的代码默认情况下将使用默认的服务帐户凭据。因此,当您为节点初始化云存储SDK或使用管理SDK时,它将以某种方式进行身份验证至少它可以从您的默认存储桶读写。在我看来,不使用默认存储桶可能会使您的工作更加困难。即使您确实想使用另一个服务帐户,您也不必做任何真正困难的事情来让它工作。初始化一个服务器SDK非常简单,无需工作我可以在Firebase上使用sdk使用云存储。我只是不确定该设备如何连接到云存储以下载文件和/或如何从node.js sdk获取仅对服务帐户专用的下载URL。好的,所以,发送服务通常不是一个好主意在一个设备上,ICE帐户将在你不信任的人手中结束。如果你这么做了,你已经基本上把这个王国的密钥交给了任何一个愿意从设备中提取它的人。而不是让设备直接上传到存储,考虑创建你自己的API端点,这给了你更好的数量。控制设备能做什么和不能做什么。@DougStevenson我显然对安全性不太在行。我想我只是不知道如何保护现场的物联网设备。我使用Firebase函数作为端点,但如何保护与该设备的连接?我一直回到这个serviceAccount。我看到了对“主机名”的引用:“storage.googleapis.com”,“title”:“Cloud storage API”,在API-index-v1.json中:这可能表明这是可能的,但我还没有幸运地将JWT用于存储,但已经成功访问了healthcare.googleapis.com
'''
This program lists lists the Google Compute Engine Instances in one zone
'''

import time
import json
import jwt
import requests
import httplib2

# Project ID for this request.
project = 'development-123456'

# The name of the zone for this request.
zone = 'us-west1-a'

# Service Account Credentials, Json format
json_filename = 'service-account.json'

# Permissions to request for Access Token
scopes = "https://www.googleapis.com/auth/cloud-platform"

# Set how long this token will be valid in seconds
expires_in = 3600   # Expires in 1 hour

def load_json_credentials(filename):
    ''' Load the Google Service Account Credentials from Json file '''

    with open(filename, 'r') as f:
        data = f.read()

    return json.loads(data)

def load_private_key(json_cred):
    ''' Return the private key from the json credentials '''

    return json_cred['private_key']

def create_signed_jwt(pkey, pkey_id, email, scope):
    '''
    Create a Signed JWT from a service account Json credentials file
    This Signed JWT will later be exchanged for an Access Token
    '''

    # Google Endpoint for creating OAuth 2.0 Access Tokens from Signed-JWT
    auth_url = "https://www.googleapis.com/oauth2/v4/token"

    issued = int(time.time())
    expires = issued + expires_in   # expires_in is in seconds

    # Note: this token expires and cannot be refreshed. The token must be recreated

    # JWT Headers
    additional_headers = {
            'kid': pkey_id,
            "alg": "RS256",
            "typ": "JWT"    # Google uses SHA256withRSA
    }

    # JWT Payload
    payload = {
        "iss": email,       # Issuer claim
        "sub": email,       # Issuer claim
        "aud": auth_url,    # Audience claim
        "iat": issued,      # Issued At claim
        "exp": expires,     # Expire time
        "scope": scope      # Permissions
    }

    # Encode the headers and payload and sign creating a Signed JWT (JWS)
    sig = jwt.encode(payload, pkey, algorithm="RS256", headers=additional_headers)

    return sig

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

def gce_list_instances(accessToken):
    '''
    This functions lists the Google Compute Engine Instances in one zone
    '''

    # Endpoint that we will call
    url = "https://www.googleapis.com/compute/v1/projects/" + project + "/zones/" + zone + "/instances"

    # One of the headers is "Authorization: Bearer $TOKEN"
    headers = {
        "Host": "www.googleapis.com",
        "Authorization": "Bearer " + accessToken,
        "Content-Type": "application/json"
    }

    h = httplib2.Http()

    resp, content = h.request(uri=url, method="GET", headers=headers)

    status = int(resp.status)

    if status < 200 or status >= 300:
        print('Error: HTTP Request failed')
        return

    j = json.loads(content.decode('utf-8').replace('\n', ''))

    print('Compute instances in zone', zone)
    print('------------------------------------------------------------')
    for item in j['items']:
        print(item['name'])

if __name__ == '__main__':
    cred = load_json_credentials(json_filename)

    private_key = load_private_key(cred)

    s_jwt = create_signed_jwt(
            private_key,
            cred['private_key_id'],
            cred['client_email'],
            scopes)

    token, err = exchangeJwtForAccessToken(s_jwt)

    if token is None:
        print('Error:', err)
        exit(1)

    gce_list_instances(token)
# Create the HTTP url for the Google Storage REST API
url = "https://www.googleapis.com/storage/v1/b?project=" + project

resp, content = h.request(uri=url, method="GET", headers=headers)

s = content.decode('utf-8').replace('\n', '')

j = json.loads(s)

print('')
print('Buckets')
print('----------------------------------------')
for item in j['items']:
    print(item['name'])