python - 使用带有 GCS 的签名 URL 时签名不匹配

标签 python google-app-engine google-cloud-storage

我打算让我的应用程序的用户直接将文件上传到 GCS,为此我使用 PUT request带有签名的网址。我的 python 代码如下所示:

def get(self):
    base_url = 'https://storage.googleapis.com/' + self.get_bucket_name()
    expiration = utils.unix_time_secs(datetime.now() + timedelta(hours=1))

    string_to_sign = (  'PUT' + "\n" +
                        '' + "\n" +
                        '' + "\n" +
                        str(expiration) + "\n" +
                        '' + "\n" +
                        'my-bucket.appspot.com')

    signed_string = app_identity.sign_blob(string_to_sign)[1]
    signature = base64.b64encode(signed_string)
    signature = urllib.quote(signature, safe='')
    google_access_id = app_identity.get_service_account_name()

    params = '?GoogleAccessId={0}&Expires={1}&Signature={2}'.format(
                        google_access_id, expiration, signature)


    self.template_values['base_url'] = base_url
    self.template_values['params'] = params
    self.render('testA.html')

在客户端,我使用 XMLHttpRequest 将文件发送到 GCS:

function gCloud (base_url, params, callback) {
    var xhr = new XMLHttpRequest();
    var file = document.querySelector('#fotos').files[0]

    //Here goes an event listener (callback)

    url = base_url + '/' + file.name + params
    xhr.open("PUT", url, true);
    xhr.setRequestHeader("Content-Type", file.type)
    xhr.send(file)
}

问题是,当我发送此请求时,我收到 403 禁止错误,指出计算出的签名与我提供的签名不匹配。这可能是一个愚蠢的错误,但我无法让它工作。可能是什么问题?

编辑:

我尝试使用 google-cloud-python,并尝试了这段代码(有效):

def get(self):
    bucket = storage.Client().get_bucket(self.get_bucket_name())
    blob = bucket.blob('Yosemite.jpg')

    expiration = utils.unix_time_secs(datetime.now() + timedelta(hours=1))
    base_url = blob.generate_signed_url(expiration, "PUT", 'image/jpeg')
    self.template_values['base_url'] = base_url
    self.render('testA.html')

现在的问题是,由于 'generate_signed_url' 是 Blob 类的一种方法,它已经假设我知道创建 url 的对象的唯一路径,如 blob = bucket.blob('Yosemite.jpg'),我不这么认为。如果用户想上传Space.jpg怎么办? Yosemite.jpg 不会创建 my-bucket/Space.jpg,而是会被覆盖,因此我将拥有 Yosemite.jpg,如果我打开图像,它实际上将是 Space.jpg。我该如何解决这个问题?

另外,如果我不在“generate_signed_url”函数中包含 Content-Type='image-jpeg',签名将不匹配。如果用户改为上传 png 会怎样?

最佳答案

URL 签名代码很棘手,而且非常难以调试。幸运的是,Google 的 google-cloud 库有一个“generate_signed_url”函数可以为您处理这个问题。我强烈鼓励您使用它而不是自己重写它。 Here's the documentation .

即使您不想使用它,您也可能需要查看 implementation of the signing logic in Python

现在,如果您想自己调试它,检查错误消息非常有用。它将包括服务器检查签名的字符串的完整副本。打印您的“string_to_sign”并查看它是否与从服务器返回的值匹配。如果没有,那是你的问题。如果是,请继续进行实际签名。

查看您的代码,我猜测问题可能是您没有对 google_access_id 进行网址转义,但我不确定。

关于python - 使用带有 GCS 的签名 URL 时签名不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44642123/

相关文章:

google-api - 如何启用 Google 云存储免费配额计费

java - 使用元素计数通过 Dataflow 写入 GCS

python - 如何使用不同的步骤迭代两个文件而不使用python将它们全部加载到内存中?

python - 删除重复项,保留最新日期,Pandas 数据框

python - 蒙特卡洛飞镖模拟器

java - Sitemesh 2.4 与 GAE

python - 通过网页形式将图像上传到谷歌云存储Python,谷歌应用程序引擎,AngularJS

java - 将照片从 Android 应用上传到 Google Cloud Storage/App Engine - 非法字符 '_'

python - 停留在下一个字母生成器——Python 3

java - 401 错误未知授权检索谷歌日历