Подписанные URL-адреса Google Cloud Storage с Google App Engine
Это неудобно иметь дело с регулярными Подписанными URL-адресами (проверка подлинности строки запроса) для облачного хранилища Google.
Пример URL-адресов Google Cloud Storage → Это действительно единственный код, доступный во всем Интернете для создания подписанных URL-адресов для Google Cloud Storage? Должен ли я читать все это и при необходимости адаптировать его вручную для GAE Pure Python при необходимости?
Это смешно, когда вы сравниваете его с AWS S3 getAuthenticatedURL(), уже включенным в любой SDK...
Я пропустил что-то очевидное или все сталкиваются с той же проблемой? Какая сделка?
Ответы
Ответ 1
Вот как это сделать в Go:
func GenerateSignedURLs(c appengine.Context, host, resource string, expiry time.Time, httpVerb, contentMD5, contentType string) (string, error) {
sa, err := appengine.ServiceAccount(c)
if err != nil {
return "", err
}
expUnix := expiry.Unix()
expStr := strconv.FormatInt(expUnix, 10)
sl := []string{
httpVerb,
contentMD5,
contentType,
expStr,
resource,
}
unsigned := strings.Join(sl, "\n")
_, b, err := appengine.SignBytes(c, []byte(unsigned))
if err != nil {
return "", err
}
sig := base64.StdEncoding.EncodeToString(b)
p := url.Values{
"GoogleAccessId": {sa},
"Expires": {expStr},
"Signature": {sig},
}
return fmt.Sprintf("%s%s?%s", host, resource, p.Encode()), err
}
Ответ 2
Недавно я столкнулся с этой проблемой и нашел решение сделать это в python в GAE, используя встроенную учетную запись службы. Используйте функцию sign_blob() в пакете google.appengine.api.app_identity, чтобы подписать строку подписи и использовать get_service_account_name() в том же пакете, чтобы получите значение для GoogleAccessId.
Не знаю, почему это так плохо документировано, даже зная теперь, что это работает, я не могу найти подсказки с помощью поиска Google, чтобы для этой цели можно было использовать встроенную учетную запись. Очень приятно, что он работает, хотя!
Ответ 3
Отъезд https://github.com/GoogleCloudPlatform/gcloud-python/pull/56
В Python это делает...
import base64
import time
import urllib
from datetime import datetime, timedelta
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from OpenSSL import crypto
method = 'GET'
resource = '/bucket-name/key-name'
content_md5, content_type = None, None
expiration = datetime.utcnow() + timedelta(hours=2)
expiration = int(time.mktime(expiration.timetuple()))
# Generate the string to sign.
signature_string = '\n'.join([
method,
content_md5 or '',
content_type or '',
str(expiration),
resource])
# Take our PKCS12 (.p12) key and make it into a RSA key we can use...
private_key = open('/path/to/your-key.p12', 'rb').read()
pkcs12 = crypto.load_pkcs12(private_key, 'notasecret')
pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey())
pem_key = RSA.importKey(pem)
# Sign the string with the RSA key.
signer = PKCS1_v1_5.new(pem_key)
signature_hash = SHA256.new(signature_string)
signature_bytes = signer.sign(signature_hash)
signature = base64.b64encode(signature_bytes)
# Set the right query parameters.
query_params = {'GoogleAccessId': '[email protected]',
'Expires': str(expiration),
'Signature': signature}
# Return the built URL.
return '{endpoint}{resource}?{querystring}'.format(
endpoint=self.API_ACCESS_ENDPOINT, resource=resource,
querystring=urllib.urlencode(query_params))
Ответ 4
И если вы не хотите писать его по собственному заказу этого класса на GitHub.
Действительно прост в использовании
GCSSignedUrlGenerator
Ответ 5
Я понятия не имею, почему документы настолько плохи. Единственный другой всеобъемлющий ответ на SO - отличный, но утомительный.
Введите метод generate_signed_url. При сканировании кроличьей скважины вы заметите, что путь кода при использовании этого метода совпадает с решением в вышеприведенном столбце SO при выполнении в GAE. Однако этот метод менее утомительный, поддерживает другие среды и имеет лучшие сообщения об ошибках.
В коде:
def sign_url(obj, expires_after_seconds=60):
client = storage.Client()
default_bucket = '%s.appspot.com' % app_identity.get_application_id()
bucket = client.get_bucket(default_bucket)
blob = storage.Blob(obj, bucket)
expiration_time = int(time.time() + expires_after_seconds)
url = blob.generate_signed_url(expiration_time)
return url