服務端簽名直傳並設置上傳回調php
請參考 Web端直傳實踐 裏的背景介紹。html
當採用服務端簽名後直傳方案後,問題來了,用戶上傳數據後,不少場景下,應用服務器都要知道用戶上傳了哪些文件,文件名字,甚至若是是圖片的話,圖片的大小等。爲此OSS開發了上傳回調功能。java
簡單講,就是用戶要上載一個文件到OSS服務器,並且但願上載完畢的時候本身的應用服務可以知道這件事,這時就須要設置一個回調函數,把這件事告知用戶的應用服務器。這樣當OSS收到用戶的上傳請求以後,開始上傳,傳完以後不會直接給用戶返回結果,而是先通知用戶的應用服務器:「我上傳完畢了」,而後應用服務器告訴OSS:「我知道啦,你幫我轉達給個人主人吧」,因而OSS就把結果轉達給用戶了。python
用戶電腦瀏覽器測試樣例:點擊這裏體驗上傳回調示例git
用手機測試該上傳是否有效。能夠用手機(微信、QQ、手機瀏覽器等)掃一掃二維碼試試(這個不是廣告,只是上述網址的二維碼,爲了讓你們看一下這個實現能在手機端完美運行)。github
點擊這裏:oss-h5-upload-js-php-callback.zipweb
例子是採用後端簽名,語言是用PHP。shell
其餘語言的用法:apache
oss-h5-upload-js-php-callback.zip
裏面的upload.js, 將裏面的變量severUrl改爲第2步部署的地址。如severUrl = http://1.2.3.4:8080
或者serverUrl= http://abc.com/post/
只要如下三步,就能實現文件快速經過網頁上傳到OSS,而且OSS會回調通知到用戶設置的應用服務器。json
php/get.php
,將變量$id設成AccessKeyId,$key設置成AccessKeySecret,$host設置成 bucket+endpoint。
$id= 'xxxxxx';
$key= 'xxxxx';
$host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com
爲了瀏覽安全,必須爲bucket設置Cors, 參照下文。
設置本身的回調URL,如http://abc.com/test.html
(必須公網訪問得通),即本身的回調服務器地址,OSS會在文件上傳完成後,把文件上傳信息,經過本身設置的回調URL(http://abc.com/test.html
)發送給應用服務器。
設置方法:修改php/get.php,(這個回調服務端代碼實例參考下文)
$callbackUrl = "http://abc.com/test.html";
這個例子裏面更多細節 ,如上傳簽名,設置隨機文件名等更多細節能夠參照:點擊這裏,瞭解上傳更多細節。
下面講解一下核心邏輯。
代碼要添加的內容以下:
new_multipart_params = {
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
'success_action_status' : '200', //讓服務端返回200,否則,默認會返回204
'callback': callbackbody,
'signature': signature,
};
上述的callbackbody 是php服務端返回的。在本例中,從後端php取到的內容以下:
{"accessid":"6MKOqxGiGU4AUk44",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDo1MjoyOVoiLCJjdb25kaXRpb25zIjpbWyJjdb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
"signature":"VsxOcOudxDbtNSvz93CLaXPz+4s=",
"expire":1446727949,
"callback":"eyJjYWxsYmFja1VybCI6Imh0dHA6Ly9vc3MtZGVtby5hbGl5dW5jcy5jdb206MjM0NTAiLCJjYWxsYmFja0hvc3QiOiJvc3MtZGVtby5hbGl5dW5jcy5jdb20iLCJjYWxsYmFja0JvZHkiOiJmaWxlbmFtZT0ke29iamVjdH0mc2l6ZT0ke3NpemV9Jm1pbWVUeXBlPSR7bWltZVR5cGV9JmhlaWdodD0ke2ltYWdlSW5mby5oZWlnaHR9JndpZHRoPSR7aW1hZ2VJdbmZvLndpZHRofSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==","dir":"user-dirs/"}
上面提到callbackbody,就是上述返回結果裏面的callback內容,通過base64編碼後生成的。
解碼後的內容以下:
{"callbackUrl":"http://oss-demo.aliyuncs.com:23450",
"callbackHost":"oss-demo.aliyuncs.com",
"callbackBody":"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}",
"callbackBodyType":"application/x-www-form-urlencoded"}
內容的解析以下:
在用戶的請求邏輯中,很重要的地方就是第4步和第5步,OSS與應用服務器交互的時候,用戶可能會有如下疑問:
問題1:若是我是開發者,那麼我要怎麼樣確認請求是從OSS發送過來的呢?
答案:OSS發送請求時,會跟應用服務器構造簽名。二者經過簽名保證。
問題2:這個簽名是怎麼作的?或者有示例代碼嗎?
答案:有的。上面的例子裏面是Callback應用服務器的例子:
http://oss-demo.aliyuncs.com:23450
(目前只支持Linux)。
上面運行的代碼是:callback_app_server.py.zip
運行方案:在Linux下面直接執行裏面的文件python callback_app_server.py
便可,程序自實現了一個簡單的http server,運行該程序可能須要安裝rsa的依賴。
問題3: 爲什麼個人應用服務器收到的回調請求沒有Authotization頭?
答案:有些 Web server會將Authorization頭自行解析掉,好比apache2,所以須要設置成不解析這個頭部。以apache2爲例,具體設置方法爲:
- 打開rewrite模塊,執行命令:a2enmod rewrite;
- 修改配置文件
/etc/apache2/apache2.conf
(apache2的安裝路徑不一樣會有不同)。將Allow Override設置成All,而後添加下面兩條配置:
RewriteEngine on
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
示例程序只是完成了如何檢查應用服務器收到的簽名, 用戶要自行增長對應用服務器收到回調的內容的格式解析 。
Java版本:
java -jar oss-callback-server-demo.jar 9000
(9000就運行的端口,能夠本身指定)
注意這個jar例子在java 1.7運行經過,若是有問題能夠本身依據提供的代碼進行修改。這是一個maven項目。
PHP版本:
Python版本:
Ruby版本:
https://help.aliyun.com/document_detail/31927.html
受權給第三方上傳
在典型的C/S系統架構中,服務器端負責接收並處理客戶端的請求。那麼考慮一個使用OSS做爲後端的存儲服務,客戶端將要上傳的文件發送給服務器端,而後服務器端再將數據轉發上傳到OSS。在這個過程當中,一份數據須要在網絡上傳輸兩次,一次從客戶端到服務器端,一次從服務器端到OSS。當訪問量很大的時候,服務器端須要有足夠的帶寬資源來知足多個客戶端的同時上傳的需求,這對架構的伸縮性提出了挑戰。
爲了解決這種場景帶來的挑戰,OSS提供了受權給第三方上傳的功能。使用這個功能,每一個客戶端能夠直接將文件上傳到OSS而不是經過服務器端轉發,節省了自建服務器的成本,而且充分利用了OSS的海量數據處理能力,無需考慮帶寬和併發限制等,可讓客戶專心於業務處理。
目前受權上傳能夠由兩種實現方式:
URL簽名是受權訪問的一種方式,即在請求的URL中帶OSS AccessKeyId和Signature字段,這樣用戶就能夠直接使用該URL來進行上傳。每一個URL簽名中攜帶有過時時間以保證安全。具體的作法能夠參考在URL中包含簽名。
臨時訪問憑證是經過阿里雲Security Token Service(STS)來實現受權的一種方式。其實現請參見STS Java SDK。臨時訪問憑證的流程以下:
https://help.aliyun.com/document_detail/31852.html?spm=5176.doc50092.6.593.oLFTdy
OSS在文件上傳完成的時,能夠提供回調(Callback)給用戶的回調服務器(Callback Server)。在上傳請求中攜帶相應的回調參數,即能實現上傳回調。支持上傳回調的 API 接口有:PutObject、PostObject、CompleteMultipartUpload。
提示:回調服務器(Callback Server),有時也叫業務服務器。
OSS上傳回調流程,以下圖:
-----------1---------> -----------2--------->
客戶端(SDK/PostObject) OSS 回調服務器(Callback Server)
<----------4---------- <----------3----------
上圖中的數據流含義,見下表:
數據流 | 含義 | 說明 |
---|---|---|
1 | 上傳文件並攜帶回調參數[1] | 經過SDK(PutObject、CompleteMultipartUpload)/PostObject完成 |
2 | OSS存儲文件後發起回調 | OSS向上傳請求中指定的 CallbackUrl 發起 POST [2] 請求,回調超時時間是 5秒 [3] |
3 | 回調服務器返回處理結果 | 回調服務器返回的消息體必定要是 Json 格式;OSS認爲非 200 請求爲回調失敗[4] |
4 | OSS返回上傳、回調結果 | 上傳、回調都成功,返回 200 ;上傳成功、回調失敗返回 203 , ErrorCode爲 CallbackFailed , ErrorMessage描述錯誤緣由 |
注:
- [1] 格式請參看
SDK/PostObject
;- [2] 回調請求POST的格式詳見 發起回調請求;
- [3] 超時時間爲固定值,不支持配置;
- [4] 參數無效、認真失敗返回
40x
,超時、沒法鏈接返回50x
。
上傳時能夠經過設置回調參數,指定回調服務器URL、發送給回調服務器的數據、格式等。回調服務器處理回調時,須要一些上下文信息,如bucket
、object
等,經過系統變量指定;系統變量之外的上下文信息,經過自定義變量指定。
上傳回調包括如下參數:
字段 | 含義 | 說明 |
---|---|---|
callbackUrl | 回調服務器地址 | 必選參數 |
callbackHost | 回調請求消息頭中Host 的值 |
可選參數,默認爲callbackUrl |
callbackBody | 回調請求的消息體 | 必選參數,內容能夠包括系統變量和自定義變量 |
callbackBodyType | 回調請求消息頭中Content-Type 的值,即callbackBody 的數據格式 |
可選參數,支持application/x-www-form-urlencoded 和application/json ,默認爲前者 |
上傳回調參數經過上傳請求攜帶,有兩種方式:
x-oss-callback
,攜帶回調參數。這種方式比較經常使用,推薦該方式;callback
,攜帶回調參數。x-oss-callback
或 callback
的值生成規則以下:
Callback := Base64(CallbackJson)
CallbackJson := '{' CallbackUrlItem, CallbackBodyItem [, CallbackHostItem, CallbackBodyTypeItem] '}'
CallbackUrlItem := '"'callbackUrl'"' ':' '"'CallbackUrlValue'"'
CallbackBodyItem := '"'callbackBody'"' ':' '"'CallbackBodyValue'"'
CallbackHostItem := '"'callbackHost'"' ':' '"'CallbackHostValue'"'
CallbackBodyTypeItem := '"'callbackBodyType'"' : '"'CallbackBodyType'"'
CallbackBodyType := application/x-www-form-urlencoded | application/json
CallbackJson
的值示例以下:
{
"callbackUrl" : "http://abc.com/test.php",
"callbackHost" : "oss-cn-hangzhou.aliyuncs.com",
"callbackBody" : "{\"bucket\":${mimeType}, \"object\":${object},\"size\":${size},\"mimeType\":${mimeType},\"my_var\":${x:my_var}}",
"callbackBodyType" : "application/json"
}
或
{
"callbackUrl" : "http://abc.com/test.php",
"callbackBody" : "bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&my_var=${x:my_var}"
}
/*callback: new Buffer(JSON.stringify({ 'callbackUrl':'http://abc.com/test.php', 'callbackBody':'bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}'})).toString('base64'),*/ JSONObject call = new JSONObject(); call.put("callbackUrl", "http://abc.com/test.php"); call.put("callbackBody", "bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}"); byte[] bytes = Base64.encodeBase64(call.toString().getBytes()); String callback = new String(bytes);
CallbackJson
示例中的 callbackBody
包括如 ${bucket}
、${object}
、${size}
的變量,即爲OSS定義的系統變量,OSS回調時會用實際值替換掉系統變量。OSS定義的系統變量以下表:
變量 | 含義 |
---|---|
${bucket} | 存儲空間名稱 |
${object} | 文件名稱 |
${etag} | 文件的ETag |
${size} | 文件大小 |
${mimeType} | 文件類型,如image/jpeg等 |
${imageInfo.height} | 圖片高度 |
${imageInfo.width} | 圖片寬度 |
${imageInfo.format} | 圖片格式,如jpg、png等 |
注意:
- 系統變量大小寫敏感;
- 系統的變量的格式必定是
${bucket}
;- imageInfo針對於圖片格式,爲非圖片格式值爲空。
CallbackJson
示例中的 callbackBody
包括如 ${x:my_var}
的變量,即自定義變量,OSS回調時會用自定義的值替換掉自定義變量。自定義變量的值能夠在上傳請求中定義並攜帶,有如下兩種方式:
x-oss-callback-var
,攜帶自定義變量。這種方式比較經常使用,也是推薦方式;callback-var
,攜帶自定義變量。x-oss-callback-var
或 callback-var
的生成規則以下:
CallbackVar := Base64(CallbackVarJson)
CallbackVarJson := '{' CallbackVarItem [, CallbackVarItem]* '}'
CallbackVarItem := '"''x:'VarName'"' : '"'VarValue'"'
CallbackVarJson
的值示例以下:
{
"x:my_var1" : "value1",
"x:my_var2" : "value2"
}
注意:
- 自定義變量必須以 x: 開頭,大小寫敏感,格式是
${x:my_var}
;- 自定義變量的長度受消息頭、URL的長度限制,建議自定義變量不超過10個,總長度不超過512Byte。
部分SDK對上述步驟進行了封裝,如Java、JS;部分SDK須要使用上面的規則生成上傳回調參數和自定義變量,如Python、PHP、C。SDK的使用示例以下:
SDK | 上傳回調示例 | 說明 |
---|---|---|
Java | CallbackSample.java | 注意CallbackBody 中的轉義字符 |
Python | object_callback.py | |
PHP | Callback.php | 注意上傳的$options中OSS_CALLBACK 、OSS_CALLBACK_VAR 不須要base64,SDK會作 |
JS | object.test.js | |
C | oss_callback_sample.c | |
Ruby | callback.rb | |
iOS | 上傳後回調通知 | 注意<var1> 的格式應該是x:var1 |
Andriod | 上傳後回調通知 | 注意CallbackBody 中的轉義字符 |
注意:C# SDK、Go SDK尚不支持上傳回調。
PostObject支持上傳回調,回調參數經過表單域callback
攜帶,自定義變量經過獨立的表單域攜帶,詳細請參看 PostObjet。PostObject的使用示例以下:
SDK | 上傳回調示例 |
---|---|
Java | PostObjectSample.java |
Python | object_post.py |
JS | JavaScript客戶端簽名直傳 |
C# | PostPolicySample.cs |
回調服務器(Callback Server),是一個HTTP服務器,處理OSS發送的回調請求,POST消息。回調服務器的URL即上傳回調參數中的 callbackUrl
。回調服務器是用戶本身實現的處理邏輯,實現上傳數據的記錄、審查、處理、統計等。
回調服務器爲了確認收到的POST請求,來自於OSS的上傳回調,須要驗證該POST消息的簽名。固然,回調服務器也能夠不驗證簽名,直接處理該消息。爲了提升回調服務器的安全性,建議驗證消息簽名。回調簽名規則請參看回調簽名。
提示:OSS的回調服務器示例中提供了簽名校驗的實現,推薦直接使用該部分代碼。
回調服務器的主要邏輯,對OSS的回調請求進行處理。如下幾點請注意:
回調服務器的實現示例以下:
語言 | 示例 | 運行方法 |
---|---|---|
Java | AppCallbackServer.zip | 解壓後執行java -jar oss-callback-server-demo.jar 9000 |
PHP | callback-php-demo.zip | Apache環境下部署運行 |
Python | callback_app_server.py.zip | 解壓後執行python callback_app_server.py |
Ruby | oss-callback-server | 執行ruby aliyun_oss_callback_server.rb |
上傳回調的調試分爲兩部分,上傳的客戶端、處理回調的回調服務器。建議先調試客戶端上傳部分,再調試回調服務器部分。兩部分單獨調試完成後,再運行完整的上傳回調。
客戶端調試時,可以使用OSS提供的回調服務器 http://callback.oss-demo.com:23450,即回調參數 callbackUrl
。該回調服務器只驗證回調請求的簽名,對回調請求不作處理。對於簽名驗證成功的回調請求,返回 {"Status":"OK"}
;簽名驗證失敗的回調請求,返回 400 Bad Request
;非POST請求返回501 Unsupported method
。示例回調服務器的代碼請參看callback_app_server.py.zip
回調服務器是一個支持處理 POST 請求的 HTTP 服務器,能夠在OSS提供的示例基礎上修改,也能夠本身獨立實現。OSS提供的回調服務器示例:
語言 | 示例 | 運行方法 |
---|---|---|
Java | AppCallbackServer.zip | 解壓後執行java -jar oss-callback-server-demo.jar 9000 |
PHP | callback-php-demo.zip | Apache環境下部署運行 |
Python | callback_app_server.py.zip | 解壓後執行python callback_app_server.py |
Ruby | oss-callback-server | 執行ruby aliyun_oss_callback_server.rb |
回調服務器能夠經過 cRUL 命令調試,下面幾個命令可能會用到:
# 向回調服務器發送消息體爲 `object=test_obj` 的 `POST` 請求,可使用以下命令
curl -d "object=test_obj" http://callback.oss-demo.com:23450 -v
# 向回調服務器發送消息體爲文件 `post.txt` 內容 的 `POST` 請求,可使用以下命令
curl -d @post.txt http://callback.oss-demo.com:23450 -v
#向回調服務器發送消息體爲文件 `post.txt` 內容的 `POST` 請求,並攜帶指定的消息頭 `Content-Type`
curl -d @post.txt -H "Content-Type: application/json" http://callback.oss-demo.com:23450 -v
注意:
- 調試回調服務器時,能夠先忽略簽名驗證部分,由於
cURL
模擬簽名功能比較困難;- 簽名驗證功能OSS示例中已經提供,建議直接使用;
- 回調服務器建議有日誌功能,記錄收到的全部消息,方便調試、跟蹤;
- 回調服務器正確處理回調請求後,必定要返回 200,而不是其它的
20x
;- 回調服務器返回給OSS的消息體,必定要是 JSON 格式,
Content-Type
設置爲application/json
。
<Error>
<Code>InvalidArgument</Code>
<Message>The callback configuration is not json format.</Message>
<RequestId>587C79A3DD373E2676F73ECE</RequestId>
<HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
<ArgumentName>callback</ArgumentName>
<ArgumentValue>{"callbackUrl":"8.8.8.8:9090","callbackBody":"{"bucket":${bucket},"object":${object}}","callbackBodyType":"application/json"}</ArgumentValue>
</Error>
緣由:回調參數設置錯誤,或參數格式錯誤。常見的錯誤是ArgumentValue
之間的回調參數,不是有效 JSON 格式。在 JSON 中 \
、"
是轉移字符,如 "callbackBody":"{"bucket":${bucket},"object":${object}}"
應該爲 "callbackBody":"{\"bucket\":${bucket},\"object\":${object}}"
。針對具體的SDK,請參看對應的上傳回調示例,詳細請參考 SDK使用示例
部分。
轉義後的字符 | 轉義前的字符 |
---|---|
\\ | \\\\ |
「 | \\\」 |
\b | \\b |
\f | \\f |
\n | \\n |
\r | \\r |
\t | \\t |
<Error>
<Code>CallbackFailed</Code>
<Message>Response body is not valid json format.</Message>
<RequestId>587C81A125F797621829923D</RequestId>
<HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
</Error>
緣由:回調服務器返回給OSS的消息體非 JSON 格式。您能夠經過 curl -d "<Content>" <CallbackServerURL> -v
或抓包確認內容。Windows下推薦使用工具 Wireshark 抓包,Linux下使用命令 tcpdump 抓包。一些非法返回消息體以下:OK
,\357\273\277{"Status":"OK"}
(即含有ef bb bf
三個字節的BOM頭)等
<Error>
<Code>CallbackFailed</Code>
<Message>Error status : -1.OSS can not connect to your callbackUrl, please check it.</Message>
<RequestId>587C8735355BE8694A8E9100</RequestId>
<HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
</Error>
緣由:回調服務器處理時間超過 5秒,OSS認爲超時。建議回調服務器的處理邏輯修改成異步,保證在5秒內處理完畢並返回結果OSS。
<Error>
<Code>CallbackFailed</Code>
<Message>Error status : -1 8.8.8.8:9090 reply timeout, cost:5000ms, timeout:5000ms (err -4, errno115)</Message>
<RequestId>587C8D382AE0B92FA3EEF62C</RequestId>
<HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
</Error>
緣由:回調服務器處理時間超過5秒,OSS認爲超時。
<Error>
<Code>CallbackFailed</Code>
<Message>Error status : 400.</Message>
<RequestId>587C89A02AE0B92FA3C7981D</RequestId>
<HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
</Error>
緣由:回調服務器返回給OSS的消息的狀態碼是400
,請檢查回調服務器的處理邏輯。
<Error>
<Code>CallbackFailed</Code>
<Message>Error status : 502.</Message>
<RequestId>587C8D382AE0B92FA3EEF62C</RequestId>
<HostId>bucket.oss-cn-hangzhou.aliyuncs.com</HostId>
</Error>
緣由:回調服務器未啓動,或者上傳回調參數中的CallbackUrl
,或者OSS與回調服務器的網絡不通。推薦在ECS上部署回調服務器,與OSS同屬內網能夠節省流量費用,同時保證網絡質量。
https://help.aliyun.com/document_detail/50092.html?spm=5176.doc27781.6.1050.pxQLlA