2017-06-20 15 views
1

私のアプリケーションのユーザーが署名付きURLを使用してPUT requestを使用しているGCSに直接ファイルをアップロードできるようにします。 Pythonで私のコードは次のようになります。クライアント側で署名付きURLをGCSで使用すると署名が一致しない

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') 

、私はGCSにファイルを送信するためにはXMLHttpRequestを使用します。

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禁止されたエラー計算された署名が私が提供したものと一致しなかったというエラー。おそらくダムのエラーですが、これを動作させることはできません。何が問題なの?

EDIT:

私はGoogleがクラウドの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 = bucket.blob( 'Yosemite.jpg')のように、URLを作成するオブジェクトの一意のパスを知っていると仮定しています。 Space.jpgをアップロードしたい場合はどうなりますか? my-bucket/Space.jpgを作成する代わりに、Yosemite.jpgが上書きされますので、Yosemite.jpgを開き、イメージを開くと実際はSpace.jpgになります。これをどうすれば解決できますか?

また、 'generate_signed_url'関数にContent-Type = 'image-jpeg'を含めないと、署名が一致しません。ユーザーが代わりにpngをアップロードするとどうなりますか?

答えて

2

URL署名コードは難解で悪名高いほどデバッグが難しいです。幸いにも、Googleのgoogle-cloudライブラリには、これを処理する "generate_signed_url"関数があります。私はあなた自身で書き直すのではなく、それを使うことを強く勧めます。 Here's the documentation

使用したくない場合でも、implementation of the signing logic in Pythonをチェックアウトしてください。

ここで、自分でデバッグしたい場合は、エラーメッセージを確認すると非常に便利です。サーバーが署名をチェックした文字列の完全なコピーが含まれます。あなたの "string_to_sign"を印刷して、それがサーバから戻ってきた値と一致するかどうか確認してください。そうでなければ、それはあなたの問題です。そうであれば、実際の署名に移動します。

コードを確認すると、あなたのgoogle_access_idをURLエスケープしていない可能性がありますが、わかりません。

+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

正規のリソースのobjectname部分が欠落しているようです。これは必須ですが、ユーザーがファイル名を選択できるPUT署名付きURLを作成することはできません。URLに署名するときに指定する必要があります。

また、SignatureDoesNotMatchエラーの応答の本文には、文字列ToSign(別名、サーバーが署名する予定の文字列)を含むXMLが含まれている必要があります。これを実際に署名した文字列と比較すると、不一致がどこから来ているのかを判断できます。

+0

あなたは正しい、それは問題の一部でした。もう1つはContent-Typeで、オプションであるとはいえ、それを動作させるために指定する必要がありました。とにかく、私はそれを世話するクライアントライブラリを使用しています。あなたの答えに感謝します。 –

+0

content-typeはオプションですが、PUTリクエストは署名と一致する必要があります。署名にcontent-typeを指定しない場合、PUTリクエストにはcontent-typeも含まれていない必要があります。 –