2017-06-20 159 views
1

我打算讓我的應用程序的用戶直接將文件上載到GCS,爲此我使用帶有簽名url的PUT request。我在Python代碼如下所示:使用帶有GCS的帶簽名的Url不匹配簽名不匹配

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表示計算出的簽名與我提供的簽名不匹配的禁止錯誤。這可能是一個愚蠢的錯誤,但我無法得到這個工作。可能是什麼問題呢?

編輯:

我嘗試使用谷歌-雲蟒,並試圖此代碼(其工作):

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」是斑點的方法它已經假設我知道創建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會怎樣?

回答

2

URL簽名代碼非常棘手,且難以調試。幸運的是,Google的谷歌雲庫有一個「generate_signed_url」函數,可以爲您提供幫助。我強烈建議你使用它,而不是自己重寫它。 Here's the documentation

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

現在,如果你想自己調試它,檢查錯誤信息是非常有用的。它將包括服務器檢查簽名的字符串的完整副本。打印您的「string_to_sign」並查看它是否與從服務器返回的值相匹配。如果沒有,那就是你的問題。如果是這樣,請繼續進行實際簽名。

查看您的代碼,我的猜測是,問題可能是您不是URL轉義您的google_access_id,但我不確定。

+0

感謝您的回答。我嘗試轉義google_access_id,但沒有解決它。我試着使用google-cloud-python庫並相應地編輯了這個問題,但是根據我的理解,該函數假設我知道諸如文件名或它的內容類型之類的內容,然後將url傳遞給用戶。我能做些什麼呢? –

+1

OH。您正在嘗試編輯客戶端的URL以追加對象名稱?這是行不通的。整點簽名的URL是爲了防止客戶編輯請求。如果您希望用戶能夠指定對象名稱,那麼還有另一個選項:簽名的POST請求策略文檔。請參閱https://cloud.google.com/storage/docs/xml-api/post-object#policydocument –

+0

對於這種情況,我會使用POST,我會爲其他人保留PUT。非常感謝您的回答! –

1

它看起來像缺少規範資源的對象名部分。這是必需的,不可能創建允許用戶選擇文件名的PUT簽名URL,您必須在簽名URL時指定它。

另請注意,SignatureDoesNotMatch錯誤的響應正文應包含一些帶有stringToSign的XML - 也就是服務器期望您簽名的字符串。您可以將其與您實際簽署的字符串進行比較,以確定不匹配的來源。

+0

你是對的,那是問題的一部分。另一件事是內容類型,我不得不指定它使它工作,即使它應該是可選的。無論如何,我現在正在使用客戶端庫,它負責照顧它。非常感謝您的回答。 –

+0

content-type是可選的,但PUT請求必須與簽名匹配。如果您不在簽名中指定內容類型,則PUT請求也不能包含內容類型。 –