閱讀337 返回首頁    go 小米6


Callback__關於Object操作_API 參考_對象存儲 OSS-阿裏雲

上傳回調

用戶隻需要在發送給OSS的請求中攜帶相應的Callback參數,即能實現回調。現在支持CallBack的API 接口有:PutObject、PostObject、CompleteMultipartUpload。

構造CallBack參數

CallBack參數是由一段經過base64編碼的Json字串,用戶關鍵需要指定請求回調的服務器URL(callbackUrl)以及回調的內容(callbackBody)。詳細的Json字段如下:

字段 含義
callbackUrl - 文件上傳成功後OSS向此url發送回調請求,請求方法為POST,body為callbackBody指定的內容。正常情況下,該url需要響應“HTTP/1.1 200 OK”,body必須為JSON格式,響應頭Content-Length必須為合法的值,且不超過3MB。
- 支持同時配置最多5個url,以”;”分割。OSS會依次發送請求直到第一個返回成功為止。
- 如果沒有配置或者值為空則認為沒有配置callback。
- 支持HTTPS地址。
- 為了保證正確處理中文等情況,callbackUrl需做url編碼處理,比如https://example.com/中文.php?key=value&中文名稱=中文值 需要編碼成 https://example.com/%E4%B8%AD%E6%96%87.php?key=value&%E4%B8%AD%E6%96%87%E5%90%8D%E7%A7%B0=%E4%B8%AD%E6%96%87%E5%80%BC
必選項
callbackHost - 發起回調請求時Host頭的值,隻有在設置了callbackUrl時才有效。
- 如果沒有配置 callbckHost,則會解析callbackUrl中的url並將解析出的host填充到callbackHost中
可選項
callbackBody - 發起回調時請求body的值,例如:key=$(key)&etag=$(etag)&my_var=$(x:my_var)。
- 支持OSS係統變量、自定義變量和常量,支持的係統變量如下表所示 。自定義變量的支持方式在PutObject和CompleteMultipart中是通過callback-var來傳遞,在PostObject中則是將各個變量通過表單域來傳遞。
必選項
callbackBodyType - 發起回調請求的Content-Type,支持application/x-www-form-urlencoded和application/json,默認為前者。
- 如果為application/x-www-form-urlencoded,則callbackBody中的變量將會被經過url編碼的值替換掉,如果為application/json,則會按照json格式替換其中的變量。
可選項

示例json串如下

  1. {
  2. "callbackUrl":"121.101.166.30/test.php",
  3. "callbackHost":"oss-cn-hangzhou.aliyuncs.com",
  4. "callbackBody":"{"mimeType":${mimeType},"size":${size}}",
  5. "callbackBodyType":"application/json"
  6. }
  1. {
  2. "callbackUrl":"121.43.113.8:23456/index.html",
  3. "callbackBody":"bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}&my_var=${x:my_var}"
  4. }

其中callbackBody中可以設置的係統變量有,其中imageInfo針對於圖片格式,如果為非圖片格式都為空:

係統變量 含義
bucket bucket
object object
etag 文件的etag,即返回給用戶的etag字段
size object大小,CompleteMultipartUpload時為整個object的大小
mimeType 資源類型,如jpeg圖片的資源類型為image/jpeg
imageInfo.height 圖片高度
imageInfo.width 圖片寬度
imageInfo.format 圖片格式,如jpg、png等

自定義參數

用戶可以通過callback-var參數來配置自定義參數。

自定義參數是一個Key-Value的Map,用戶可以配置自己需要的參數到這個Map。在OSS發起POST回調請求的時候,會將這些參數和上一節所述的係統參數一起放在POST請求的body中以方便接收回調方獲取。

構造自定義參數的方法和callback參數的方法是一樣的,也是以json格式來傳遞。該json字符串就是一個包含所有自定義參數的Key-Value的Map。這裏有個特別需要注意的地方就是,用戶自定義參數的Key一定要以x:開頭,且必須為小寫。否則OSS會返回錯誤。假定用戶需要設定兩個自定義的參數分別為x:var1和x:var2,對應的值分別為value1和value2,那麼構造出來的json格式如下:

  1. {
  2. "x:var1":"value1",
  3. "x:var2":"值2"
  4. }

構造回調請求

構造完成上述的callback和callback-var兩個參數之後,一共有三種方式傳給OSS。其中callback為必填參數,callback-var為可選參數,如果沒有自定義參數的話可以不用添加callback-var字段。這三種方式如下:

  • 在URL中攜帶參數。
  • 在Header中攜帶參數。
  • 在POST請求的body中使用表單域來攜帶參數,在使用POST請求上傳Object的時候隻能使用這種方式來指定回調參數。

這三種方式隻能同時使用其中一種,否則OSS會返回InvalidArgument錯誤。

要將參數附加到OSS的請求中,首先要將上文構造的json字符串使用base64編碼,然後按照如下的方法附加到OSS的請求中:

  • 如果在URL中攜帶參數。把callback=[CallBack]或者callback-var=[CallBackVar]作為一個url參數帶入請求發送。計算簽名CanonicalizedResource時 ,將callback或者callback-var當做一個sub-resource計算在內
  • 如果在Header中攜帶參數。把x-oss-callback=[CallBack]或者x-oss-callback-var=[CallBackVar]作為一個head帶入請求發送。在計算簽名CanonicalizedOSSHeaders時,將x-oss-callback-var和x-oss-callback計算在內。如下示例:
  1. PUT /test.txt HTTP/1.1
  2. Host: callback-test.oss-test.aliyun-inc.com
  3. Accept-ncoding: identity
  4. Content-Length: 5
  5. x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0=
  6. User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4)
  7. x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0=
  8. Host: callback-test.oss-test.aliyun-inc.com
  9. Expect: 100-Continue
  10. Date: Mon, 14 Sep 2015 12:37:27 GMT
  11. Content-Type: text/plain
  12. Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4=
  13. Test
  • 如果需要在POST上傳Object的時候附帶回調參數會稍微複雜一點,callback參數要使用獨立的表單域來附加,如下麵的示例:
  1. --9431149156168
  2. Content-Disposition: form-data; name="callback"
  3. eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==

如果擁有自定義參數的話,不能直接將callback-var參數直接附加到表單域中,每個自定義的參數都需要使用獨立的表單域來附加,舉個例子,如果用戶的自定義參數的json為

  1. {
  2. "x:var1":"value1",
  3. "x:var2":"value2"
  4. }

那麼POST請求的表單域應該如下:

  1. --9431149156168
  2. Content-Disposition: form-data; name="callback"
  3. eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==
  4. --9431149156168
  5. Content-Disposition: form-data; name="x:var1"
  6. value1
  7. --9431149156168
  8. Content-Disposition: form-data; name="x:var2"
  9. value2

同時可以在policy中添加callback條件(如果不添加callback,則不對該參數做上傳驗證)如:

  1. { "expiration": "2014-12-01T12:00:00.000Z",
  2. "conditions": [
  3. {"bucket": "johnsmith" },
  4. {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="},
  5. ["starts-with", "$key", "user/eric/"],
  6. ]
  7. }

發起回調請求

如果文件上傳成功,OSS會根據用戶的請求中的callback參數和自定義參數(callback-var參數),將特定內容以POST方式發送給應用服務器。

  1. POST /index.html HTTP/1.0
  2. Host: 121.43.113.8
  3. Connection: close
  4. Content-Length: 181
  5. Content-Type: application/x-www-form-urlencoded
  6. User-Agent: ehttp-client/0.0.1
  7. bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test

返回回調結果

比如應用服務器端返回的回應請求為:

  1. HTTP/1.0 200 OK
  2. Server: BaseHTTP/0.3 Python/2.7.6
  3. Date: Mon, 14 Sep 2015 12:37:27 GMT
  4. Content-Type: application/json
  5. Content-Length: 9
  6. {"a":"b"}

返回上傳結果

再給客戶端的內容為:

  1. HTTP/1.1 200 OK
  2. Date: Mon, 14 Sep 2015 12:37:27 GMT
  3. Content-Type: application/json
  4. Content-Length: 9
  5. Connection: keep-alive
  6. ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249"
  7. Server: AliyunOSS
  8. x-oss-bucket-version: 1442231779
  9. x-oss-request-id: 55F6BF87207FB30F2640C548
  10. {"a":"b"}

需要注意的是,如果類似CompleteMultipartUpload這樣的請求,在返回請求本身body中存在內容(如XMl格式的信息),使用上傳回調功能後會覆蓋原有的body的內容如{"a":"b"},希望對此處做好判斷處理。

回調簽名

用戶設置callback參數後,OSS將按照用戶設置的callbackUrl發送POST回調請求給用戶的應用服務器。應用服務器收到回調請求之後,如果希望驗證回調請求確實是由OSS發起的話,那麼可以通過在回調中帶上簽名來驗證OSS的身份。

生成簽名

簽名在OSS端發生,采用RSA非對稱方式簽名,私鑰加密的過程為:

  1. authorization = base64_encode(rsa_sign(private_key, url_decode(path) + query_string + ‘n’ + body, md5))

說明:其中private_key為私鑰,隻有oss知曉,path為回調請求的資源路徑,query_string為查詢字符串,body為回調的消息體,所以簽名過程由以下幾步組成:

  • 獲取待簽名字符串:資源路徑經過url解碼後,加上原始的查詢字符串,加上一個回車符,加上回調消息體
  • RSA簽名:使用秘鑰對待簽名字符串進行簽名,簽名的hash函數為md5
  • 將簽名後的結果做base64編碼,得到最終的簽名,簽名放在回調請求的authorization頭中

如下例:

  1. POST /index.php?id=1&index=2 HTTP/1.0
  2. Host: 121.43.113.8
  3. Connection: close
  4. Content-Length: 18
  5. authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA==
  6. Content-Type: application/x-www-form-urlencoded
  7. User-Agent: ehttp-client/0.0.1
  8. x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==
  9. bucket=yonghu-test

path為/index.php,query_string為?id=1&index=2,body為bucket=yonghu-test,最終簽名結果為kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA==

驗證簽名

驗證簽名的過程即為簽名的逆過程,由應用服務器驗證,過程如下:

  1. Result = rsa_verify(public_key, md5(url_decode(path) + query_string + ‘n’ + body), base64_decode(authorization))

字段的含義與簽名過程中描述相同,其中public_key為公鑰, authorization為回調頭中的簽名,整個驗證簽名的過程分為以下幾步:

  • 回調請求的x-oss-pub-key-url頭保存的是公鑰的url地址的base64編碼,因此需要對其做base64解碼後獲取到公鑰,即
  1. public_key = urlopen(base64_decode(x-oss-pub-key-url頭的值))

這裏需要注意,用戶需要校驗x-oss-pub-key-url頭的值必須以https://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/開頭,目的是為了保證這個publickey是由OSS頒發的。

  • 獲取base64解碼後的簽名
  1. signature = base64_decode(authorization頭的值)
  • 獲取待簽名字符串,方法與簽名一致
  1. sign_str = url_decode(path) + query_string + ‘n’ + body
  • 驗證簽名
  1. result = rsa_verify(public_key, md5(sign_str), signature)

以上例為例:

  • 獲取到公鑰的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==經過base64解碼後得到https://gosspublic.alicdn.com/callback_pub_key_v1.pem
  • 簽名頭kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA==做base64解碼(由於為非打印字符,無法顯示出解碼後的結果)
  • 獲取待簽名字符串,即url_decode(“index.php”) + “?id=1&index=2” + “n” + “bucket=yonghu-test”,並做md5
  • 驗證簽名

應用服務器示例

以下為一段python示例,演示了一個簡單的應用服務器,主要是說明驗證簽名的方法,此示例需要安裝M2Crypto庫

  1. import httplib
  2. import base64
  3. import md5
  4. import urllib2
  5. from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
  6. from M2Crypto import RSA
  7. from M2Crypto import BIO
  8. def get_local_ip():
  9. try:
  10. csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  11. csock.connect(('8.8.8.8', 80))
  12. (addr, port) = csock.getsockname()
  13. csock.close()
  14. return addr
  15. except socket.error:
  16. return ""
  17. class MyHTTPRequestHandler(BaseHTTPRequestHandler):
  18. '''
  19. def log_message(self, format, *args):
  20. return
  21. '''
  22. def do_POST(self):
  23. #get public key
  24. pub_key_url = ''
  25. try:
  26. pub_key_url_base64 = self.headers['x-oss-pub-key-url']
  27. pub_key_url = pub_key_url_base64.decode('base64')
  28. if not pub_key_url.startswith("https://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"):
  29. self.send_response(400)
  30. self.end_headers()
  31. return
  32. url_reader = urllib2.urlopen(pub_key_url)
  33. #you can cache it
  34. pub_key = url_reader.read()
  35. except:
  36. print 'pub_key_url : ' + pub_key_url
  37. print 'Get pub key failed!'
  38. self.send_response(400)
  39. self.end_headers()
  40. return
  41. #get authorization
  42. authorization_base64 = self.headers['authorization']
  43. authorization = authorization_base64.decode('base64')
  44. #get callback body
  45. content_length = self.headers['content-length']
  46. callback_body = self.rfile.read(int(content_length))
  47. #compose authorization string
  48. auth_str = ''
  49. pos = self.path.find('?')
  50. if -1 == pos:
  51. auth_str = urllib2.unquote(self.path) + 'n' + callback_body
  52. else:
  53. auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + 'n' + callback_body
  54. print auth_str
  55. #verify authorization
  56. auth_md5 = md5.new(auth_str).digest()
  57. bio = BIO.MemoryBuffer(pub_key)
  58. rsa_pub = RSA.load_pub_key_bio(bio)
  59. try:
  60. result = rsa_pub.verify(auth_md5, authorization, 'md5')
  61. except:
  62. result = False
  63. if not result:
  64. print 'Authorization verify failed!'
  65. print 'Public key : %s' % (pub_key)
  66. print 'Auth string : %s' % (auth_str)
  67. self.send_response(400)
  68. self.end_headers()
  69. return
  70. #do something accoding to callback_body
  71. #response to OSS
  72. resp_body = '{"Status":"OK"}'
  73. self.send_response(200)
  74. self.send_header('Content-Type', 'application/json')
  75. self.send_header('Content-Length', str(len(resp_body)))
  76. self.end_headers()
  77. self.wfile.write(resp_body)
  78. class MyHTTPServer(HTTPServer):
  79. def __init__(self, host, port):
  80. HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler)
  81. if '__main__' == __name__:
  82. server_ip = get_local_ip()
  83. server_port = 23451
  84. server = MyHTTPServer(server_ip, server_port)
  85. server.serve_forever()

其它語言實現的應用服務器如下:

Java版本

  • 下載地址:點擊這裏
  • 運行方法:解壓包運行java -jar oss-callback-server-demo.jar 9000(9000就運行的端口,可以自己指定)

PHP版本:

  • 下載地址:點擊這裏
  • 運行方法:部署到Apache環境下,因為PHP本身語言的特點,取一些數據頭部會依賴於環境。所以可以參考例子根據所在環境修改。

Python版本:

  • 下載地址:點擊這裏
  • 運行方法:解壓包直接運行python callback_app_server.py,運行該程序需要安裝rsa的依賴。

Ruby版本:

  • 下載地址:點擊這裏
  • 運行方法: ruby aliyun_oss_callback_server.rb

特別須知

  • 如果傳入的callback或者callback-var不合法,則會返回400錯誤,錯誤碼為”InvalidArgument”,不合法的情況包括以下幾類:

    • PutObject()和CompleteMultipartUpload()接口中url和header同時傳入callback(x-oss-callback)或者callback-var(x-oss-callback-var)參數
    • callback或者callback-var(PostObject()由於沒有callback-var參數,因此沒有此限製,下同)參數過長(超過5KB)
    • callback或者callback-var沒有經過base64編碼
    • callback或者callback-var經過base64解碼後不是合法的json格式
    • callback參數解析後callbackUrl字段包含的url超過限製(5個),或者url中傳入的port不合法,比如{"callbackUrl":"10.101.166.30:test", "callbackBody":"test"}
    • callback參數解析後callbackBody字段為空
    • callback參數解析後callbackBodyType字段的值不是”application/x-www-form-urlencoded”或者”application/json”
    • callback參數解析後callbackBody字段中變量的格式不合法,合法的格式為${var}
    • callback-var參數解析後不是預期的json格式,預期的格式應該為{"x:var1":"value1","x:var2":"value2"...}
  • 如果回調失敗,則返回203,錯誤碼為”CallbackFailed”,回調失敗隻是表示OSS沒有收到預期的回調響應,不代表應用服務器沒有收到回調請求(比如應用服務器返回的內容不是json格式),另外,此時文件已經成功上傳到了OSS

  • 應用服務器返回OSS的響應必須帶有Content-Length的Header,Body大小不要超過1MB。

最後更新:2016-12-14 10:26:26

  上一篇:go PostObject__關於Object操作_API 參考_對象存儲 OSS-阿裏雲
  下一篇:go PutSymlink__關於Object操作_API 參考_對象存儲 OSS-阿裏雲