2012-08-01 74 views
1

我們在S3上的自定義域中託管了大量視頻/ audo /媒體,並創建了一組函數以簽署這些URL並允許它們同時流式傳輸下載。問題是簽名的URL當然不起作用。錯誤是:Amazon S3簽名的URL以更改內容類型

我們計算的請求籤名與您提供的簽名不匹配。檢查你的密鑰和簽名方法。

當然,如果我們把從這個頁面返回的字節碼輸入到Amazon S3 Signature Tester並從那裏獲取字節碼,它就可以工作。即使從我們的函數簽名的字符串以及簽名測試程序中解碼的字節碼都是相同的,它也不會起作用。

它通過的PHP代碼的一小塊所謂:

$headers = createS3parameters($expiry, $file_type); 
$request = preg_replace("/^.*?:\/\/.*\//", "/", $bucketurl); 
$signature = signRequest($request, $expiry, $s3secret, $headers, "GET", $file_type); 
$signed_request = "$bucketurl?AWSAccessKeyId=$s3key&Expires=$expiry&$headers&Signature=$signature"; 

這是實際簽署它的功能。

function signRequest($request, $expiration, $s3secret, $headers = '', $type = 'GET', $content_type = 'default') 
{ 
    if ($expiration == 0 || $expiration == null) 
    { 
     $expiration = time() + 315576000; // 10 years (never) 
    } 

    if (strcmp($content_type, 'default') == 0) 
    { 
     $content_type = ""; 
    } 

    // S3 tester spits out this format 
    /*$string = "$type\n". 
       "\n\n". 
       "$expiration\n". 
       "/mybucket$request?$headers";*/ 

    $string = "$type\n". 
       "\n". 
       "$content_type\n". 
       "$expiration\n". 
       "$headers\n". 
       "$request"; 


    // must be in UTF8 format 
    $string = utf8_encode(trim($string)); 
    // encode to binary hash using sha1. require S3 bucket secret key 
    $hash = hash_hmac("sha1",$string, $s3secret,false); 
    // sha1 hash must be base64 encoded 
    $hash = base64_encode($hash); 
    // base64 encoded sha1 hash must be urlencoded 
    $signature = rawurlencode($hash); 

    return $signature; 
} 

,然後創建一個URL,例如:

http://mybucket.s3.amazonaws.com/My_Boring_Video.wmv?AWSAccessKeyId=AKIAIEXAMPLE6GA3WYQ&Expires=1344160808&response-content-type=application/force-download&response-expires=1344160808&Signature=OTIxOTI0YjNjMTA1NjMyNmJjYTk0MGE2YWJkMmI5OWQ3MGM2ZGY0MQ%3D%3D 

不幸的是不起作用。這裏有一個顯而易見的問題,我一直在盯着太久而無法正確理解?

+0

您使用什麼播放器播放視頻?通常玩家正在追加一些變量,這是問題 – 2012-08-01 17:41:49

+0

這個問題與流式傳輸vs下載無關。當你通過http://mybuckets.s3.amazonaws.com/My_Boring_Video.wmv訪問視頻時,它工作得很好。問題在於簽名的URL經常被AmazonS3拒絕。 – dcmbrown 2012-08-01 19:48:01

回答

2

更新:規格是規格,但只有在符合實際操作時纔有所幫助。

Amazon的S3規格說,簽名應形成如下:

Signature = URL-Encode(Base64(HMAC-SHA1(YourSecretAccessKeyID, UTF-8-Encoding-Of(StringToSign)))); 

StringToSign = HTTP-VERB + "\n" + 
    Content-MD5 + "\n" + 
    Content-Type + "\n" + 
    Expires + "\n" + 
    CanonicalizedAmzHeaders + 
    CanonicalizedResource;  

無論其實際的請求需要看起來像這樣:

StringToSign = HTTP-VERB + "\n" + 
    "\n" + 
    "\n" + 
    Expires + "\n" + 
    Bucket + CanonicalizedResource + "?" + CanonicalizedAmzHeaders; 

奇怪的是,在PHP中你也似乎無法做到這一點:

$string = "$type\n". 
      "\n". 
      "\n". 
      "$expiration\n". 
      "/$bucket$request?$headers"; 

它改變簽名並最終被拒絕,因此必須全部在一行上。我還沒有去檢查這是否是我們特定版本的PHP中的錯誤,或者只是一個普通的PHP錯誤(或者可能是功能性的!)。即使您正在使用可用的虛擬URL,例如mybucket.s3.amazonaws.com或mybucket.mydomain.com,您也必須包含您的存儲桶名稱。 documentation沒有指定你可以做什麼或不可以做什麼,我做了一個假設,因爲我們使用的是基於S3的虛榮URL,它(S3)會選擇域名並將其翻譯成存儲桶。

我最終改變我的功能如下所示:

function signRequest($bucket, $request, $expiration, $s3secret, $headers = '', $type = 'GET', $content_type = 'default') 
{ 
    if ($expiration == 0 || $expiration == null) 
    { 
     $expiration = time() + 315576000; // 10 years (never) 
    } 

    if (strcmp($content_type, 'default') == 0) 
    { 
     $content_type = ""; 
    } 

    $headers = trim($headers, '&'); 

    // This is the spec: 
    /*$string = "$type\n". 
     "\n". 
     "$content_type\n". 
     "$expiration\n". 
     "$headers\n". 
     "$bucket$request";*/ 
    // but it will only work as this 
    $string = "$type\n\n\n$expiration\n/$bucket$request?$headers"; 

    // this could be a single line of code but left otherwise for readability 
    // must be in UTF8 format 
    $string = utf8_encode(trim($string)); 
    // encode to binary hash using sha1. require S3 bucket secret key 
    $hash = hash_hmac("sha1",$string, $s3secret,true); 
    // sha1 hash must be base64 encoded 
    $hash = base64_encode($hash); 
    // base64 encoded sha1 hash must be urlencoded 
    $signature = urlencode($hash); 

    return $signature; 
} 

希望別人認爲這也是有用的。

UPDATE(20180109):添加一個函數,調用這個函數(也就是讓我們做這個很簡單)。

它有助於理解傳遞給signRequest()函數的內容。

private function genS3QueryString($bucketurl) 
    { 
      $file_type = 'application/force-download'; 
      $expiry = '1831014000'; //Sun, Jan 09 2028 0700 UTC 

      $headers = '&response-content-type='.$file_type.'&response-expires='.$expiry; 

      $bucket = preg_replace("/^.*?:\/\/(.*)\.s3\.amazonaws\.com\/.*/", "$1", $bucketurl); 
      $request = preg_replace("/^.*?:\/\/.*\//", "/", $bucketurl); 
      $signature = $this->signRequest($bucket, $request, $expiry, S3_SECRET_KEY, $headers, 'GET', $file_type); 

      $signed_request = '?AWSAccessKeyId='.S3_KEY.'&Expires='.$expiry.$headers.'&Signature='.$signature; 

      return $signed_request; 
    } 

然而,您會注意到第一個函數僅生成附加到AWS的GET請求所需的加密簽名部分。根據文檔(參見上面的鏈接),請求中需要更多內容,正如上面第二個函數所形成的。

如果您願意,我相信可以是到期時間,但應該足夠在將來使資產變得過時並在實際到期時間之前更換。選擇了一個任意時間來充分延長當前版本的網站。

第二個函數只需要受保護資源的存儲區url。所需的響應類型可以添加到呼叫中,或者簡單地更改,以便將資產(在這種情況下只是不可顯示的)文檔作爲不同類型下載。

signed_request然後必須將其返回到bucketurl上以便工作URI從受保護的S3資產請求。

+0

它沒有爲我工作:http://stackoverflow.com/questions/12079366/wrong-sha1-from-php-cf-according-to-amazon-s3 – SamGoody 2012-08-22 18:50:58

+0

你能告訴我你在$ request中傳遞了什麼,並且您創建的函數的$ bucket參數? – 2018-01-06 12:38:53

+0

您的存儲桶是您唯一的存儲桶名稱。因此,如果您通過網址https://our-site-files.s3.amazonaws.com/assets/pdfs/report.pdf訪問存儲區項目,您的存儲區就是:我們的網站文件。同樣,您的請求將是:/assets/pdfs/report.pdf – dcmbrown 2018-01-09 17:32:55