Как создать токен доступа из учетных данных учетной записи службы с помощью REST API?

Я создал учетную запись службы в Google Cloud Platform и загрузил закрытый ключ в формате JSON. Я пытаюсь создать вычислительный ресурс через REST API. Для аутентификации мне нужен AccessToken, который нужно установить в качестве заголовка REST API для создания вычислительных ресурсов. Есть ли REST API для получения токена доступа из закрытого ключа (без использования SDK или клиентов Google)?




Ответы (3)


В следующем примере показано несколько важных шагов для вызова API Google Cloud без использования SDK в Python. Подобный код работает практически на любом языке (c #, java, php, nodejs).

Измените исходный код, указав имя файла Json вашей учетной записи службы, вашу зону Google и свой идентификатор проекта.

В этом примере будут перечислены экземпляры в одной зоне для указанного проекта. Из этого примера вы узнаете, как фреймворк вызывает API для создания экземпляров GCE.

Этот код покажет вам, как:

  1. Как загрузить учетные данные учетной записи службы из файла Json.
  2. Как извлечь закрытый ключ, используемый для подписи запросов.
  3. Как создать JWT (Json Web Token) для Google Oauth 2.0.
  4. Как установить Google Scopes (разрешения).
  5. Как подписать JWT для создания подписанного JWT (JWS).
  6. Как обменять подписанный JWT на токен доступа Google OAuth 2.0.
  7. Как установить срок действия. Эта программа по умолчанию составляет 3600 секунд (1 час).
  8. Как вызвать Google API и установить заголовок авторизации.
  9. Как обработать возвращенные результаты Json и отобразить имя каждого экземпляра.

Пример программы на Python 3.x:

'''
This program lists lists the Google Compute Engine Instances in one zone
'''
# Author: John Hanley
# https://www.jhanley.com

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)

Для получения дополнительной информации посетите мой блог. Я пишу подобные статьи и публикую исходный код, чтобы помочь другим понять, как писать программное обеспечение для облака.

www.jhanley.com

person John Hanley    schedule 26.12.2018

Существует более простой способ сгенерировать токен из учетной записи службы с использованием библиотек Google.

from google.auth.transport import requests
from google.oauth2 import service_account

CREDENTIAL_SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
CREDENTIALS_KEY_PATH = '/PATH/TO/SERVICE_ACCOUNT.json'

def get_service_account_token():
  credentials = service_account.Credentials.from_service_account_file(
          CREDENTIALS_KEY_PATH, scopes=CREDENTIAL_SCOPES)
  credentials.refresh(requests.Request())
  return credentials.token

Или, если вы хотите использовать аутентификацию по умолчанию

import google
from google.auth.transport import requests

CREDENTIAL_SCOPES = ["https://www.googleapis.com/auth/cloud-platform"] 

def get_default_token():
  credentials, project_id = google.auth.default(scopes=CREDENTIAL_SCOPES)
  credentials.refresh(requests.Request())
  return credentials.token

При создании объекта учетные данные токен пуст, но после обновления учетных данных он содержит токен доступа, который можно использовать в качестве заголовка в запросах API.

person Diego Rodríguez    schedule 19.03.2020

То же решение с использованием JAVA

import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;

public class GoogleHelper {
    public static String getAccessToken() throws IOException {
        return GoogleCredentials
                .fromStream(new FileInputStream("/PATH/TO/SERVICE_ACCOUNT.json"))
                .createScoped("https://www.googleapis.com/auth/cloud-platform")
                .refreshAccessToken()
                .getTokenValue();
    }
}
person Misha Mikus    schedule 27.01.2021
comment
Удивительно, я искал этот код целых 2 дня! Не могли бы вы рассказать мне, как вы нашли этот API? Думаю, я не умею гуглить. - person kimchoky; 07.07.2021
comment
@kimchoky. на самом деле я просто преобразовал код из примеров другого языка) - person Misha Mikus; 08.07.2021