閱讀801 返回首頁    go 小米 go 小米5


服務端簽名後直傳__Web端直傳實踐_最佳實踐_對象存儲 OSS-阿裏雲

背景

請參考 Web端直傳實踐 中的背景介紹。

采用JS客戶端直接簽名有一個很嚴重的安全隱患。就是OSS AccessId/AccessKey暴露在前端頁麵。可以隨意拿到AccessId/AccessKey,這是非常不安全的做法。 本文將此例子進化,簽名及上傳policy從後端php代碼取。

請求邏輯是:

  1. 客戶端要上傳圖片時,到應用服務器取上傳的policy及簽名。
  2. 客戶端拿到簽名直接上傳到OSS。

示例

用戶電腦瀏覽器測試樣例:點擊這裏打開示例程序

用手機測試該上傳是否有效。二維碼:可以用手機(微信、QQ、手機瀏覽器等)掃一掃試試(這個不是廣告,隻是上述網址的二維碼。這為了讓大家看一下這個實現能在手機端完美運行)。

代碼下載

點擊這裏:oss-h5-upload-js-php.zip

例子采用後端簽名,語言是用PHP。

其他語言的用法:

  1. 下載對應的語言示例。
  2. 修改示例代碼,如設置監聽的端口等,然後運行。
  3. 在oss-h5-upload-js-php.zip裏麵的upload.js, 將裏麵的變量severUrl改成第二步部署的地址。如severUrl = http:/1.2.3.4:8080或者serverUrl=https://abc.com/post/

原理

上傳采用OSS PostObject方法, 用pupload在瀏覽器構造PostObject請求,發往OSS。簽名在服務端實現,即在(PHP)完成,相同道理,服務端可以以JAVA、.NET、Ruby、GO、python等語言編寫,核心邏輯就是構造Post簽名。本例子提供了JAVA、PHP例子。

請求邏輯是:

  1. 網頁通過JS 向服務端請求簽名。
  2. JS獲取到簽名後,通過pupload 上傳到OSS。

快速使用

隻要以下兩步,就能實現文件快速通過網頁上傳到OSS。

  1. 設置成自己的id、key、bucket。
    設置方法:修改php/get.php, 將變量$id設成AccessKeyId, $key設置成AccessKeySecret, $host設置成:bucket+endpoint

    說明:關於endpoint,請參見 基本概念介紹

    1. $id= 'xxxxxx';
    2. $key= 'xxxxx';
    3. $host = 'https://post-test.oss-cn-hangzhou.aliyuncs.com
  2. 為了瀏覽安全,必須為bucket設置CORS,參照下文。

以下講解一下核心邏輯。

設置成隨機文件名

有時候要把用戶上傳的文件,設置成隨機文件名,後綴保持跟客戶端文件一致。例子裏麵,通過兩個radio來區分, 如果想在上傳時,就固定設置成隨機文件名,可以將函數改成如下:

  1. function check_object_radio() {
  2. g_object_name_type = 'random_name';
  3. }

如果想在上傳時,固定設置成用戶的文件,可以將函數改成:

  1. function check_object_radio() {
  2. g_object_name_type = 'local_name';
  3. }

設置上傳目錄

上傳的目錄是由服務端(即PHP)指定的,這樣的好處就是安全。 這樣就能控製每個客戶端隻能上傳到指定的目錄,做到安全隔離。想要修改上傳目錄地址成abc/(必須以’/‘結尾),可以修改代碼裏麵的php/get.php

  1. $dir = 'abc/';

設置上傳文件過濾條件

有的時候需要設置上傳的過濾條件,如可以設置上傳隻能上傳圖片,上傳文件的大小,不能有重複上傳等。這時可以利用filters參數。

  1. var uploader = new plupload.Uploader({
  2. ……
  3. filters: {
  4. mime_types : [ //隻允許上傳圖片和zip文件
  5. { title : "Image files", extensions : "jpg,gif,png,bmp" },
  6. { title : "Zip files", extensions : "zip" }
  7. ],
  8. max_file_size : '400kb', //最大隻能上傳400kb的文件
  9. prevent_duplicates : true //不允許選取重複文件
  10. },

設置過濾條件,原理是利用plupload 的屬性filters來設置。
上述值的設置含義:

  • mime_types:限製上傳的文件後綴
  • max_file_size: 限製上傳的文件大小
  • prevent_duplicates: 限製不能重複上傳

注意:filters過濾條件不是必須的。如果不想設置過濾條件,隻要把該項注釋即可。

獲取上傳後的文件名

如果要知道文件上傳成功後的文件名,pupload 會調用FileUploaded事件。 如下:

  1. FileUploaded: function(up, file, info) {
  2. if (info.status == 200)
  3. {
  4. document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = 'upload to oss success, object name:' + get_uploaded_object_name(file.name);
  5. }
  6. else
  7. {
  8. document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = info.response;
  9. }
  10. }

可以利用如下函數,得到上傳到oss的文件名,其中file.name記錄了上傳本地文件的名字。

  1. get_uploaded_object_name(file.name)

上傳簽名

javaScript最主要是從後端取到policyBase64、accessid、signature這三個變量。 往後端取這三個變量核心代碼如下:

  1. phpUrl = './php/get.php'
  2. xmlhttp.open( "GET", phpUrl, false );
  3. xmlhttp.send( null );
  4. var obj = eval ("(" + xmlhttp.responseText+ ")");
  5. host = obj['host']
  6. policyBase64 = obj['policy']
  7. accessid = obj['accessid']
  8. signature = obj['signature']
  9. expire = parseInt(obj['expire'])
  10. key = obj['dir']

現在解析一下xmlhttp.responseText(以下僅為示例,並不一定要求是以下的格式,但是必須有signature、accessid、policy這三個值)。

  1. {"accessid":"6MKOqxGiGU4AUk44",
  2. "host":"https://post-test.oss-cn-hangzhou.aliyuncs.com",
  3. "policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
  4. "signature":"I2u57FWjTKqX/AE6doIdyff151E=",
  5. "expire":1446726203,"dir":"user-dir/"}
  • accessid: 指的用戶請求的accessid。注意僅知道accessid, 對數據不會有影響。
  • host: 指的是用戶要往哪個域名發往上傳請求。
  • policy:指的是用戶表單上傳的策略policy,是經過base64編碼過的字符串。
  • signature:是對上述第三個變量policy簽名後的字符串。
  • expire:指的是當前上傳策略失效時間,這個變量並不會發送到OSS,因為這個已經指定在policy裏麵,這個變量的含義,後麵講。

現在分析一下policy的內容,將其解碼後的內容是:

  1. {"expiration":"2015-11-05T20:23:23Z",
  2. "conditions":[["content-length-range",0,1048576000],
  3. ["starts-with","$key","user-dir/"]]

這裏有一個關鍵的地方,PolicyText指定了該Policy 上傳失效的最終時間。即在這個失效時間之前,都可以利用這個policy上傳文件,所以沒有必要每次上傳,都去後端取簽名。為了減少後端的壓力,這裏的設計思路是:初始化上傳時,每上傳一個文件後,取一次簽名。然後再上傳時,將當前時間跟簽名時間對比,看簽名時間是否失效了。如果失效了,就重新取一次簽名,如果沒有失效就不取。這裏就用到了變量expire。核心代碼如下:

  1. now = timestamp = Date.parse(new Date()) / 1000;
  2. [color=#000000]//可以判斷當前expire是否超過了當前時間,如果超過了當前時間,就重新取一下,3s 做為緩衝[/color]
  3. if (expire < now + 3)
  4. {
  5.    .....
  6.    phpUrl = './php/get.php'
  7.    xmlhttp.open( "GET", phpUrl, false );
  8.    xmlhttp.send( null );
  9.    ......
  10. }
  11. return .

上麵policy 的內容增加了starts-with,用來指定此次上傳的文件名必須是user-dir開頭(這個字符串,用戶可以自己指定)。

增加這個內容的背景是:在很多場景下,一個應用一個bucket,不同用戶的數據,為了防止數字覆蓋,每個用戶上傳到OSS的文件都可以有特定的前綴。那麼問題來了,用戶獲取到這個policy後,在失效期內都能修改上傳前綴,從而上傳到別人的目錄下。為了解決這個問題,可以設置應用服務器在上傳時就指定用戶上傳的文件必須是某個前綴。這樣如果用戶拿到了policy也沒有辦法上傳到別人的前綴上。保證了數據的安全性。

跨域安全設置

注意:一定要保證bucket屬性CORS設置支持POST方法。因為這個HTML直接上傳到OSS,會產生跨域請求。必須在bucket屬性裏麵設置允許跨域。

設置如下圖:

注意在IE低版本瀏覽器,pupload會以flash方式執行。必須設置crossdomain.xml ,設置方法可以參考:點擊這裏

總結

這個例子能做到網頁端上傳時,網頁端向服務端請求簽名,然後直接上傳,不用對服務端產生壓力。而且安全可靠。但是這個例子有一個特點,就是用戶上傳了多少文件,用戶上傳了什麼文件,用戶後端程序並不能馬上知道,如果想實時知曉用戶上傳了什麼文件,可以采用上傳回調。請參考 服務端簽名直傳並設置上傳回調

最後更新:2016-12-19 14:21:56

  上一篇:go JavaScript客戶端簽名直傳__Web端直傳實踐_最佳實踐_對象存儲 OSS-阿裏雲
  下一篇:go 服務端簽名直傳並設置上傳回調__Web端直傳實踐_最佳實踐_對象存儲 OSS-阿裏雲