• 关于

    有回调函数的php

    的搜索结果

问题

关于$.getJSON到底能不能跨域

a123456678 2019-12-01 20:22:31 841 浏览量 回答数 1

回答

Re钉钉回调加解密问题求助 官方给的加解密函数在php最新版本已经删掉了,找的替换函数,有官方的来解答下吗 ------------------------- Re钉钉回调加解密问题求助 哇塞,官方这么应付吗,程序员时间不等人

php小白菜 2019-12-01 23:56:35 0 浏览量 回答数 0

回答

5.3 新增的闭包语法中,use 用来捕捉变量到匿名函数内 匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。Example #1 匿名函数示例<?php echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); }, 'hello-world'); // 输出 helloWorld ?>闭包函数也可以作为变量的值来使用。PHP 会自动把此种表达式转换成内置类 Closure 的对象实例。

落地花开啦 2019-12-02 02:50:32 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

回答

Re怎么获取手机端钉钉定位的地址 引用第7楼识器于2016-07-15 16:12发表的  : 你好,看你自己的贴出来的代码,两个问题:第一个,首先在jsapiList里加上你要用的api:.device.geolocation.get,第二个,再dd.config之后有两个回调,一个是dd.ready,一个是dd.error,你在dd.ready回调里调用定位的jsapi。还有不清楚的可以看我们的文档上的demo [url=https://bbs.aliyun.com/job.php?action=topost&tid=288465&pid=804895][/url] 只能在dd.ready回调里面才能调用权限配置中的API? 其他位置不行吗,前提是成功dd.config,执行了dd.ready后 ------------------------- 回 6楼高科电子的帖子 钉钉 device.geolocation.get() 这个函数会在回调函数中返回 JSON位置数据,你获取到数据后,通过ajax异步发往服务器不就行了?客户端请求另一个页面的时候,服务器把获取到的数据发送到前端,前端再通过API 利用获得的位置数据调用地图API :dd.biz.map.locate()显示位置。。。。。自由发挥。

ceadelta 2019-12-02 02:43:02 0 浏览量 回答数 0

回答

分两种情况: 一、您有目标网站的修改权限,可以直接使用$.getJSON(); 二、您没有目标网站的修改权限,可以使用php的fopen()函数,然后再用jquery的$.get(); 两个函数的用法大同小异,根据您的情况,没有银行的修改权限, 那么先做一个php获取银行的汇率, $.get("get.php",{"m1":"货币1","m2":"货币2"},function(data,status){ //回调函数写这里,比如$("div").html(data); }): 答案来源于网络

养狐狸的猫 2019-12-02 02:14:14 0 浏览量 回答数 0

问题

有关于 PHP处理JSON

爵霸 2019-12-01 20:07:07 952 浏览量 回答数 1

问题

Serverless 工作流

黄一刀 2020-04-03 23:50:42 117 浏览量 回答数 1

回答

实验的时候发现是可以的,代码如下 <?php function func(){ echo "I'm a external function <br />"; } class Test { function doExternalFunction($_externalFunc, $_param){ func(); $_externalFunc($_param); } } function sayHello($_param){ echo "hello, $_param"; }; $name = "HanMeiMei"; $test = new Test(); $test->doExternalFunction("sayHello", $name); ?> 在调用Test类实例的doExternalFunction()方法时,外部的func()方法是可以运行的,因为它是全局的。但是建议不要这样使用,因为Test::doExternalFunction()对func()函数有依赖,当项目比较大时,而func()和Test类没有同时引入到一个文件中来,会引起错误。比较理想的情况是,使用回调函数的形式来调用外部函数,就像执行在Test::doExternalFunction()执行sayHello()一样

小旋风柴进 2019-12-02 01:59:37 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 本示例讲解在服务端通过PHP代码完成签名,并且服务端设置了上传后回调,然后通过表单直传数据到OSS。 背景 采用服务端签名后直传方案有个问题:用户上传数据后,很多场景下,应用服务器需要知道用户上传了哪些文件以及文件名称,如果是图片的话,还需要知道图片的大小等。为此OSS提供了上传回调方案。OSS回调完成后,应用服务器再返回结果给客户端。这样服务端就可以实时了解用户上传了什么文件。 Demo 您可以通过样例体验服务端签名直传并设置上传回调的效果:PC浏览器测试样例 原理介绍 服务端签名直传并设置上传回调的逻辑图如下: 流程如下: 用户向应用服务器请求上传Policy和回调。 应用服务器返回上传Policy和回调设置。 用户直接向OSS发送文件上传请求。 OSS根据用户的回调设置,发送回调请求给应用服务器。 应用服务器返回响应给OSS。 OSS将应用服务器返回的内容返回给用户。 说明 如果应用服务器返回成功,那么OSS就返回成功给用户;如果应用服务器返回失败,那么OSS也返回失败给用户。这样确保了用户上传成功和失败的文件,应用服务器都会收到通知。 简单讲,就是用户要上传一个文件到OSS,而且希望上传完毕的时候自己的应用服务器能够知道这件事,这时就需要设置一个回调函数,把这件事告知用户的应用服务器。这样当OSS收到用户的上传请求之后开始上传,上传完之后不会直接给用户返回结果,而是先通知用户的应用服务器,然后再把结果转达给用户。 前提条件 Web服务器已部署。 Web服务器对应的域名可通过公网访问。 步骤 1:下载并安装Plugload Plupload是一款简单易用且功能强大的文件上传工具, 支持多种上传方式,包括html5、flash、silverlight,、html4。它会智能检测当前环境,选择最适合的上传方式,并且会优先采用Html5方式。请参见Plupload官网进行下载和安装。 步骤 2:下载应用服务器代码 PHP:下载地址 Java:下载地址 Python:下载地址 Go:下载地址 步骤 3:修改配置文件 本示例采用PHP编写。将下载包解压后,修改以下文件: php/get.php文件:$id= '<yourAccessKeyId>'; $key= <yourAccessKeySecrety'; $host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com'; $callbackUrl = "http://oss-demo.aliyuncs.com:23450"; $callback_param = array('callbackUrl'=>$callbackUrl, 'callbackBody'=>'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}', 'callbackBodyType'=>"application/x-www-form-urlencoded"); $id:您的AccessKeyId $key:您的AessKeySecret $host:格式为BucketName.Endpoint,例如post-test.oss-cn-hangzhou.aliyuncs.com 说明 关于Endpoint的介绍,请参见Endpoint(访问域名)。 $callbackUrl:设置回调URL,即回调服务器地址,用于处理应用服务器与OSS之前的通信。例如http://abc.com:8080/callback.php。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。 $callback_param:可选参数,用于设置callbackBody和callbackBodyType等选项。 upload.js文件 将变量severUrl改成服务器部署的地址,用于处理浏览器和应用服务器之间的通信。例如http://abc.com:8080/oss-h5-upload-js-php-callback/get.php。 步骤 4:设置CORS HTML表单直接上传到OSS会产生跨域请求。为了浏览安全,需要为Bucket设置跨域规则(CORS),支持Post方法。 具体操作步骤请参见设置跨域访问。设置如下图所示: 说明 在低版本IE浏览器,Plupload会以Flash方式执行。您需要设置crossdomain.xml ,设置方法请参见OSS Web直传—使用Flash上传。 步骤 5:设置上传回调解析 示例程序只是完成了如何检查应用服务器收到的签名, 您需要自行增加对应用服务器收到的回调内容的格式解析 。 PHP: 应用服务器回调程序代码:下载地址 运行方法:将解压包部署到Apache环境下,因为PHP本身语言的特点,某些数据头部的获取会依赖于环境。请参考例子根据实际环境进行修改。 Java: 应用服务器回调程序代码:下载地址 运行方法:解压后运行java -jar oss-callback-server-demo.jar 9000。9000为运行的端口,可以自己指定。 说明 这个jar例子在java 1.7运行通过,如果有问题可以自己依据提供的代码进行修改。这是一个maven项目。 Python: 应用服务器回调程序代码:下载地址 运行方法:解压后直接运行python callback_app_server.py即可,程序自实现了一个简单的http server,运行该程序可能需要安装rsa的依赖。 Ruby: 应用服务器回调程序代码:下载地址 运行方法: ruby aliyun_oss_callback_server.rb 步骤 6:体验上传回调 将应用服务器代码zip包解压到web根目录下。 在Web浏览器中输入<Web应用服务器地址>/oss-h5-upload-js-php-callback/index.html,例如http://abc.com:8080/oss-h5-upload-js-php-callback/index.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"} 内容解析如下: CallbackUrl:OSS往这个机器发送的URL请求。 callbackHost:OSS发送这个请求时,请求头部所带的Host头。 callbackBody:OSS请求时,发送给应用服务器的内容,可以包括文件的名称、大小、类型。如果是图片,可以是图片的高度、宽度。 callbackBodyType:请求发送的Content-Type。 说明 您的应用服务器收到的回调请求有可能没有Authotization头,这是因为有些 Web应用服务器会将Authorization头自行解析掉,比如apache2,因此需要设置成不解析这个头部。以apache2为例,具体设置方法如下: 打开rewrite模块,执行命令:a2enmod rewrite。 修改配置文件 /etc/apache2/apache2.conf(根据apache2的安装路径不同会有不一样)。将Allow Override设置成All,然后添加以下两个配置项: RewriteEngine on RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

2019-12-01 23:13:29 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 本示例讲解在服务端通过PHP代码完成签名,并且服务端设置了上传后回调,然后通过表单直传数据到OSS。 背景 采用服务端签名后直传方案有个问题:用户上传数据后,很多场景下,应用服务器需要知道用户上传了哪些文件以及文件名称,如果是图片的话,还需要知道图片的大小等。为此OSS提供了上传回调方案。OSS回调完成后,应用服务器再返回结果给客户端。这样服务端就可以实时了解用户上传了什么文件。 Demo 您可以通过样例体验服务端签名直传并设置上传回调的效果:PC浏览器测试样例 原理介绍 服务端签名直传并设置上传回调的逻辑图如下: 流程如下: 用户向应用服务器请求上传Policy和回调。 应用服务器返回上传Policy和回调设置。 用户直接向OSS发送文件上传请求。 OSS根据用户的回调设置,发送回调请求给应用服务器。 应用服务器返回响应给OSS。 OSS将应用服务器返回的内容返回给用户。 说明 如果应用服务器返回成功,那么OSS就返回成功给用户;如果应用服务器返回失败,那么OSS也返回失败给用户。这样确保了用户上传成功和失败的文件,应用服务器都会收到通知。 简单讲,就是用户要上传一个文件到OSS,而且希望上传完毕的时候自己的应用服务器能够知道这件事,这时就需要设置一个回调函数,把这件事告知用户的应用服务器。这样当OSS收到用户的上传请求之后开始上传,上传完之后不会直接给用户返回结果,而是先通知用户的应用服务器,然后再把结果转达给用户。 前提条件 Web服务器已部署。 Web服务器对应的域名可通过公网访问。 步骤 1:下载并安装Plugload Plupload是一款简单易用且功能强大的文件上传工具, 支持多种上传方式,包括html5、flash、silverlight,、html4。它会智能检测当前环境,选择最适合的上传方式,并且会优先采用Html5方式。请参见Plupload官网进行下载和安装。 步骤 2:下载应用服务器代码 PHP:下载地址 Java:下载地址 Python:下载地址 Go:下载地址 步骤 3:修改配置文件 本示例采用PHP编写。将下载包解压后,修改以下文件: php/get.php文件:$id= '<yourAccessKeyId>'; $key= <yourAccessKeySecrety'; $host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com'; $callbackUrl = "http://oss-demo.aliyuncs.com:23450"; $callback_param = array('callbackUrl'=>$callbackUrl, 'callbackBody'=>'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}', 'callbackBodyType'=>"application/x-www-form-urlencoded"); $id:您的AccessKeyId $key:您的AessKeySecret $host:格式为BucketName.Endpoint,例如post-test.oss-cn-hangzhou.aliyuncs.com 说明 关于Endpoint的介绍,请参见Endpoint(访问域名)。 $callbackUrl:设置回调URL,即回调服务器地址,用于处理应用服务器与OSS之前的通信。例如http://abc.com:8080/callback.php。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。 $callback_param:可选参数,用于设置callbackBody和callbackBodyType等选项。 upload.js文件 将变量severUrl改成服务器部署的地址,用于处理浏览器和应用服务器之间的通信。例如http://abc.com:8080/oss-h5-upload-js-php-callback/get.php。 步骤 4:设置CORS HTML表单直接上传到OSS会产生跨域请求。为了浏览安全,需要为Bucket设置跨域规则(CORS),支持Post方法。 具体操作步骤请参见设置跨域访问。设置如下图所示: 说明 在低版本IE浏览器,Plupload会以Flash方式执行。您需要设置crossdomain.xml ,设置方法请参见OSS Web直传—使用Flash上传。 步骤 5:设置上传回调解析 示例程序只是完成了如何检查应用服务器收到的签名, 您需要自行增加对应用服务器收到的回调内容的格式解析 。 PHP: 应用服务器回调程序代码:下载地址 运行方法:将解压包部署到Apache环境下,因为PHP本身语言的特点,某些数据头部的获取会依赖于环境。请参考例子根据实际环境进行修改。 Java: 应用服务器回调程序代码:下载地址 运行方法:解压后运行java -jar oss-callback-server-demo.jar 9000。9000为运行的端口,可以自己指定。 说明 这个jar例子在java 1.7运行通过,如果有问题可以自己依据提供的代码进行修改。这是一个maven项目。 Python: 应用服务器回调程序代码:下载地址 运行方法:解压后直接运行python callback_app_server.py即可,程序自实现了一个简单的http server,运行该程序可能需要安装rsa的依赖。 Ruby: 应用服务器回调程序代码:下载地址 运行方法: ruby aliyun_oss_callback_server.rb 步骤 6:体验上传回调 将应用服务器代码zip包解压到web根目录下。 在Web浏览器中输入<Web应用服务器地址>/oss-h5-upload-js-php-callback/index.html,例如http://abc.com:8080/oss-h5-upload-js-php-callback/index.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"} 内容解析如下: CallbackUrl:OSS往这个机器发送的URL请求。 callbackHost:OSS发送这个请求时,请求头部所带的Host头。 callbackBody:OSS请求时,发送给应用服务器的内容,可以包括文件的名称、大小、类型。如果是图片,可以是图片的高度、宽度。 callbackBodyType:请求发送的Content-Type。 说明 您的应用服务器收到的回调请求有可能没有Authotization头,这是因为有些 Web应用服务器会将Authorization头自行解析掉,比如apache2,因此需要设置成不解析这个头部。以apache2为例,具体设置方法如下: 打开rewrite模块,执行命令:a2enmod rewrite。 修改配置文件 /etc/apache2/apache2.conf(根据apache2的安装路径不同会有不一样)。将Allow Override设置成All,然后添加以下两个配置项: RewriteEngine on RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

2019-12-01 23:13:29 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 本示例讲解在服务端通过PHP代码完成签名,并且服务端设置了上传后回调,然后通过表单直传数据到OSS。 背景 采用服务端签名后直传方案有个问题:用户上传数据后,很多场景下,应用服务器需要知道用户上传了哪些文件以及文件名称,如果是图片的话,还需要知道图片的大小等。为此OSS提供了上传回调方案。OSS回调完成后,应用服务器再返回结果给客户端。这样服务端就可以实时了解用户上传了什么文件。 Demo 您可以通过样例体验服务端签名直传并设置上传回调的效果:PC浏览器测试样例 原理介绍 服务端签名直传并设置上传回调的逻辑图如下: 流程如下: 用户向应用服务器请求上传Policy和回调。 应用服务器返回上传Policy和回调设置。 用户直接向OSS发送文件上传请求。 OSS根据用户的回调设置,发送回调请求给应用服务器。 应用服务器返回响应给OSS。 OSS将应用服务器返回的内容返回给用户。 说明 如果应用服务器返回成功,那么OSS就返回成功给用户;如果应用服务器返回失败,那么OSS也返回失败给用户。这样确保了用户上传成功和失败的文件,应用服务器都会收到通知。 简单讲,就是用户要上传一个文件到OSS,而且希望上传完毕的时候自己的应用服务器能够知道这件事,这时就需要设置一个回调函数,把这件事告知用户的应用服务器。这样当OSS收到用户的上传请求之后开始上传,上传完之后不会直接给用户返回结果,而是先通知用户的应用服务器,然后再把结果转达给用户。 前提条件 Web服务器已部署。 Web服务器对应的域名可通过公网访问。 步骤 1:下载并安装Plugload Plupload是一款简单易用且功能强大的文件上传工具, 支持多种上传方式,包括html5、flash、silverlight,、html4。它会智能检测当前环境,选择最适合的上传方式,并且会优先采用Html5方式。请参见Plupload官网进行下载和安装。 步骤 2:下载应用服务器代码 PHP:下载地址 Java:下载地址 Python:下载地址 Go:下载地址 步骤 3:修改配置文件 本示例采用PHP编写。将下载包解压后,修改以下文件: php/get.php文件:$id= '<yourAccessKeyId>'; $key= <yourAccessKeySecrety'; $host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com'; $callbackUrl = "http://oss-demo.aliyuncs.com:23450"; $callback_param = array('callbackUrl'=>$callbackUrl, 'callbackBody'=>'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}', 'callbackBodyType'=>"application/x-www-form-urlencoded"); $id:您的AccessKeyId $key:您的AessKeySecret $host:格式为BucketName.Endpoint,例如post-test.oss-cn-hangzhou.aliyuncs.com 说明 关于Endpoint的介绍,请参见Endpoint(访问域名)。 $callbackUrl:设置回调URL,即回调服务器地址,用于处理应用服务器与OSS之前的通信。例如http://abc.com:8080/callback.php。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。 $callback_param:可选参数,用于设置callbackBody和callbackBodyType等选项。 upload.js文件 将变量severUrl改成服务器部署的地址,用于处理浏览器和应用服务器之间的通信。例如http://abc.com:8080/oss-h5-upload-js-php-callback/get.php。 步骤 4:设置CORS HTML表单直接上传到OSS会产生跨域请求。为了浏览安全,需要为Bucket设置跨域规则(CORS),支持Post方法。 具体操作步骤请参见设置跨域访问。设置如下图所示: 说明 在低版本IE浏览器,Plupload会以Flash方式执行。您需要设置crossdomain.xml ,设置方法请参见OSS Web直传—使用Flash上传。 步骤 5:设置上传回调解析 示例程序只是完成了如何检查应用服务器收到的签名, 您需要自行增加对应用服务器收到的回调内容的格式解析 。 PHP: 应用服务器回调程序代码:下载地址 运行方法:将解压包部署到Apache环境下,因为PHP本身语言的特点,某些数据头部的获取会依赖于环境。请参考例子根据实际环境进行修改。 Java: 应用服务器回调程序代码:下载地址 运行方法:解压后运行java -jar oss-callback-server-demo.jar 9000。9000为运行的端口,可以自己指定。 说明 这个jar例子在java 1.7运行通过,如果有问题可以自己依据提供的代码进行修改。这是一个maven项目。 Python: 应用服务器回调程序代码:下载地址 运行方法:解压后直接运行python callback_app_server.py即可,程序自实现了一个简单的http server,运行该程序可能需要安装rsa的依赖。 Ruby: 应用服务器回调程序代码:下载地址 运行方法: ruby aliyun_oss_callback_server.rb 步骤 6:体验上传回调 将应用服务器代码zip包解压到web根目录下。 在Web浏览器中输入<Web应用服务器地址>/oss-h5-upload-js-php-callback/index.html,例如http://abc.com:8080/oss-h5-upload-js-php-callback/index.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"} 内容解析如下: CallbackUrl:OSS往这个机器发送的URL请求。 callbackHost:OSS发送这个请求时,请求头部所带的Host头。 callbackBody:OSS请求时,发送给应用服务器的内容,可以包括文件的名称、大小、类型。如果是图片,可以是图片的高度、宽度。 callbackBodyType:请求发送的Content-Type。 说明 您的应用服务器收到的回调请求有可能没有Authotization头,这是因为有些 Web应用服务器会将Authorization头自行解析掉,比如apache2,因此需要设置成不解析这个头部。以apache2为例,具体设置方法如下: 打开rewrite模块,执行命令:a2enmod rewrite。 修改配置文件 /etc/apache2/apache2.conf(根据apache2的安装路径不同会有不一样)。将Allow Override设置成All,然后添加以下两个配置项: RewriteEngine on RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

2019-12-01 23:13:29 0 浏览量 回答数 0

问题

php jQuery 实现无刷新验证问题

小旋风柴进 2019-12-01 20:09:40 815 浏览量 回答数 1

问题

求教uploadify3.1的formdata问题,我想传个额外的值给服务器

a123456678 2019-12-01 20:19:22 931 浏览量 回答数 1

回答

首先,解析 HTML 有两种办法: 一种是基于事件回调的:比如 python 的 HTMLParser 和 PHP 的 SimpleXML。它在遇到 starttag、endtag 以及遇到 text node 时就调用你指定的函数。 第二种是建立起一个完整的 DOM 树,让你进行 DOM 操作。 现在的问题就是应该怎么定义“不同”。 如果你指的只是 text node 的 text content 不同,而 node tree 的结构完全相同的话,就很容易了,此时你可以使用第一种解析 HTML 的方法;只需要把第一个页面所有的 text node 按顺序存下来;然后把第二个 HTML 的所有 text node 与之一一比对就 ok 了。 如果页面结构都不同的话,就会复杂些。这种情况下可以用第二种解析 HTML 的方法建立起 DOM 树;然后可以一层一层递归地比对innerHTML:具体来说,比如你先比对的 innerHTML。如果不一致的话,就一一比对其下所有元素的innerHTML。然后把其中innerHTML相同的节点忽略;对innerHTML不相同的节点再递归地进行处理。如果遇到某个元素在两个文档里的childNode数量不一的情况……那就看你想怎么办了。

落地花开啦 2019-12-02 02:49:42 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:53 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:50 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:52 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:52 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:52 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:53 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:52 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:52 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 用户只需要在发送给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编码处理,比如http://example.com/中文.php?key=value&中文名称=中文值 需要编码成 http://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串如下 { "callbackUrl":"121.101.166.30/test.php", "callbackHost":"oss-cn-hangzhou.aliyuncs.com", "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", "callbackBodyType":"application/json" } { "callbackUrl":"121.43.113.8:23456/index.html", "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}" } 其中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格式如下: { "x:var1":"value1", "x:var2":"value2" } 构造回调请求 构造完成上述的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计算在内。如下示例:PUT /test.txt HTTP/1.1 Host: callback-test.oss-test.aliyun-inc.com Accept-ncoding: identity Content-Length: 5 x-oss-callback-var: eyJ4Om15X3ZhciI6ImZvci1jYWxsYmFjay10ZXN0In0= User-Agent: aliyun-sdk-python/0.4.0 (Linux/2.6.32-220.23.2.ali1089.el5.x86_64/x86_64;2.5.4) x-oss-callback: eyJjYWxsYmFja1VybCI6IjEyMS40My4xMTMuODoyMzQ1Ni9pbmRleC5odG1sIiwgICJjYWxsYmFja0JvZHkiOiJidWNrZXQ9JHtidWNrZXR9Jm9iamVjdD0ke29iamVjdH0mZXRhZz0ke2V0YWd9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZpbWFnZUluZm8uaGVpZ2h0PSR7aW1hZ2VJbmZvLmhlaWdodH0maW1hZ2VJbmZvLndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSZpbWFnZUluZm8uZm9ybWF0PSR7aW1hZ2VJbmZvLmZvcm1hdH0mbXlfdmFyPSR7eDpteV92YXJ9In0= Host: callback-test.oss-test.aliyun-inc.com Expect: 100-Continue Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: text/plain Authorization: OSS mlepou3zr4u7b14:5a74vhd4UXpmyuudV14Kaen5cY4= Test 如果需要在POST上传Object的时候附带回调参数会稍微复杂一点,callback参数要使用独立的表单域来附加,如下面的示例:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==如果拥有自定义参数的话,不能直接将callback-var参数直接附加到表单域中,每个自定义的参数都需要使用独立的表单域来附加,举个例子,如果用户的自定义参数的json为{ "x:var1":"value1", "x:var2":"value2" }那么POST请求的表单域应该如下:--9431149156168 Content-Disposition: form-data; name="callback" eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkmdGFibGU9JHt4OnRhYmxlfSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ== --9431149156168 Content-Disposition: form-data; name="x:var1" value1 --9431149156168 Content-Disposition: form-data; name="x:var2" value2同时可以在policy中添加callback条件(如果不添加callback,则不对该参数做上传验证)如:{ "expiration": "2014-12-01T12:00:00.000Z", "conditions": [ {"bucket": "johnsmith" }, {"callback": "eyJjYWxsYmFja1VybCI6IjEwLjEwMS4xNjYuMzA6ODA4My9jYWxsYmFjay5waHAiLCJjYWxsYmFja0hvc3QiOiIxMC4xMDEuMTY2LjMwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JChmaWxlbmFtZSkiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIn0="}, ["starts-with", "$key", "user/eric/"], ] } 发起回调请求 如果文件上传成功,OSS会根据用户的请求中的callback参数和自定义参数(callback-var参数),将特定内容以POST方式发送给应用服务器。 POST /index.html HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 181 Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 bucket=callback-test&object=test.txt&etag=D8E8FCA2DC0F896FD7CB4CB0031BA249&size=5&mimeType=text%2Fplain&imageInfo.height=&imageInfo.width=&imageInfo.format=&x:var1=for-callback-test 返回回调结果 比如应用服务器端返回的回应请求为: HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.6 Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 {"a":"b"} 返回上传结果 再给客户端的内容为: HTTP/1.1 200 OK Date: Mon, 14 Sep 2015 12:37:27 GMT Content-Type: application/json Content-Length: 9 Connection: keep-alive ETag: "D8E8FCA2DC0F896FD7CB4CB0031BA249" Server: AliyunOSS x-oss-bucket-version: 1442231779 x-oss-request-id: 55F6BF87207FB30F2640C548 {"a":"b"} 需要注意的是,如果类似CompleteMultipartUpload这样的请求,在返回请求本身body中存在内容(如XMl格式的信息),使用上传回调功能后会覆盖原有的body的内容如{"a":"b"},希望对此处做好判断处理。 回调签名 用户设置callback参数后,OSS将按照用户设置的callbackUrl发送POST回调请求给用户的应用服务器。应用服务器收到回调请求之后,如果希望验证回调请求确实是由OSS发起的话,那么可以通过在回调中带上签名来验证OSS的身份。 生成签名 签名在OSS端发生,采用RSA非对称方式签名,私钥加密的过程为: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头中 如下例:POST /index.php?id=1&index=2 HTTP/1.0 Host: 121.43.113.8 Connection: close Content-Length: 18 authorization: kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== Content-Type: application/x-www-form-urlencoded User-Agent: ehttp-client/0.0.1 x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ== bucket=yonghu-testpath为/index.php,query_string为?id=1&index=2,body为bucket=yonghu-test,最终签名结果为kKQeGTRccDKyHB3H9vF+xYMSrmhMZjzzl2/kdD1ktNVgbWEfYTQG0G2SU/RaHBovRCE8OkQDjC3uG33esH2txA== 验证签名 验证签名的过程即为签名的逆过程,由应用服务器验证,过程如下: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解码后获取到公钥,即public_key = urlopen(base64_decode(x-oss-pub-key-url头的值))这里需要注意,用户需要校验x-oss-pub-key-url头的值必须以http://gosspublic.alicdn.com/或者https://gosspublic.alicdn.com/开头,目的是为了保证这个publickey是由OSS颁发的。 获取base64解码后的签名signature = base64_decode(authorization头的值) 获取待签名字符串,方法与签名一致sign_str = url_decode(path) + query_string + ‘\n’ + body 验证签名result = rsa_verify(public_key, md5(sign_str), signature) 以上例为例: 获取到公钥的url地址,即aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnBlbQ==经过base64解码后得到http://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库import httplib import base64 import md5 import urllib2 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from M2Crypto import RSA from M2Crypto import BIO def get_local_ip(): try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('8.8.8.8', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return "" class MyHTTPRequestHandler(BaseHTTPRequestHandler): ''' def log_message(self, format, *args): return ''' def do_POST(self): #get public key pub_key_url = '' try: pub_key_url_base64 = self.headers['x-oss-pub-key-url'] pub_key_url = pub_key_url_base64.decode('base64') if not pub_key_url.startswith("http://gosspublic.alicdn.com/") and not pub_key_url.startswith("https://gosspublic.alicdn.com/"): self.send_response(400) self.end_headers() return url_reader = urllib2.urlopen(pub_key_url) #you can cache it pub_key = url_reader.read() except: print 'pub_key_url : ' + pub_key_url print 'Get pub key failed!' self.send_response(400) self.end_headers() return #get authorization authorization_base64 = self.headers['authorization'] authorization = authorization_base64.decode('base64') #get callback body content_length = self.headers['content-length'] callback_body = self.rfile.read(int(content_length)) #compose authorization string auth_str = '' pos = self.path.find('?') if -1 == pos: auth_str = urllib2.unquote(self.path) + '\n' + callback_body else: auth_str = urllib2.unquote(self.path[0:pos]) + self.path[pos:] + '\n' + callback_body print auth_str #verify authorization auth_md5 = md5.new(auth_str).digest() bio = BIO.MemoryBuffer(pub_key) rsa_pub = RSA.load_pub_key_bio(bio) try: result = rsa_pub.verify(auth_md5, authorization, 'md5') except: result = False if not result: print 'Authorization verify failed!' print 'Public key : %s' % (pub_key) print 'Auth string : %s' % (auth_str) self.send_response(400) self.end_headers() return #do something accoding to callback_body #response to OSS resp_body = '{"Status":"OK"}' self.send_response(200) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(resp_body))) self.end_headers() self.wfile.write(resp_body) class MyHTTPServer(HTTPServer): def __init__(self, host, port): HTTPServer.__init__(self, (host, port), MyHTTPRequestHandler) if '__main__' == __name__: server_ip = get_local_ip() server_port = 23451 server = MyHTTPServer(server_ip, server_port) server.serve_forever()其它语言实现的应用服务器如下: Java版本: 下载地址:点击这里 运行方法:解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定) PHP版本: 下载地址:点击这里 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子根据所在环境修改。 Python版本: 下载地址:点击这里 运行方法:解压包直接运行python callback_app_server.py,运行该程序需要安装rsa的依赖。 C#版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 Go版本: 下载地址:点击这里 运行方法:解压后参看 README.md。 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。 Callback支持的地域 Callback目前支持的地域如下:华北 2(北京)、华东 1(杭州)、华北 1(青岛)、华东 2(上海)、上海金融云、华南 1(深圳)、香港、华北 5(呼和浩特)、华北 3(张家口)、中东东部 1(迪拜)、亚太东北 1(日本)、欧洲中部 1(法兰克福)、亚太东南 1(新加坡)、美国东部 1(弗吉尼亚)、美国西部 1(硅谷)、亚太东南 2 (悉尼)以及亚太东南 3(吉隆坡)。

2019-12-01 23:13:51 0 浏览量 回答数 0

回答

基本的一维数组 $array = array(3, 5, 2, 8); 适用的排序功能: sort rsort asort arsort natsort natcasesort ksort krsort 两者之间的区别仅在于是否保留键-值关联(“ a“函数),是否按从低到高或反向(” r“)排序,对值或键进行排序(” k“)以及如何比较值(“ nat”与正常)。有关概述以及指向更多详细信息的链接,请参见http://php.net/manual/zh-CN/array.sorting.php。 多维数组,包括对象数组 $array = array( array('foo' => 'bar', 'baz' => 42), array('foo' => ..., 'baz' => ...), ... ); 如果要按$array每个条目的键'foo' 进行排序,则需要一个自定义比较函数。上面sort和相关的函数处理简单的值,他们知道如何比较和排序。PHP不会简单地“知道”如何处理复杂的值,例如array('foo' => 'bar', 'baz' => 42);所以你需要告诉它。 为此,您需要创建一个比较函数。该函数需要两个元素,并且0如果这些元素被认为相等,则必须返回,该值要比0第一个值低时要低,而比第一个值高时要0高。这就是所需要的: function cmp(array $a, array $b) { if ($a['foo'] < $b['foo']) { return -1; } else if ($a['foo'] > $b['foo']) { return 1; } else { return 0; } } 通常,您将需要使用匿名函数作为回调。如果要使用方法或静态方法,请参见在PHP中指定回调的其他方法。 然后,您可以使用以下功能之一: usort uasort uksort 同样,它们只是在保留键值关联以及按值或键排序方面有所不同。阅读他们的文档以了解详细信息。 用法示例: usort($array, 'cmp'); usort将从数组中取出两项,并cmp用它们调用函数。因此cmp()将与$aas array('foo' => 'bar', 'baz' => 42)和$bas 一起调用array('foo' => ..., 'baz' => ...)。然后,函数返回到usort哪个值较大或它们是否相等。usort重复此过程,为传递不同的值$a,$b直到对数组排序为止。该cmp函数将被调用多次,至少被调用的次数与中的值的调用次数相同,并且每次调用的值的$array组合不同。$a$b 要习惯这个想法,请尝试以下操作: function cmp($a, $b) { echo 'cmp called with $a:', PHP_EOL; var_dump($a); echo 'and $b:', PHP_EOL; var_dump($b); } 您要做的就是定义一种自定义方式来比较两个项目,这就是您所需要的。这适用于各种价值。 顺便说一下,这适用于任何值,这些值不必是复杂的数组。如果您要进行自定义比较,则也可以对简单的数字数组进行比较。 sort 按引用排序,不返回任何有用的信息! 请注意,数组在适当的位置排序,您无需将返回值分配给任何对象。$array = sort($array)将用替换该数组true,而不用已排序的数组。只是sort($array);作品。 自定义数值比较 如果要按baz数字键进行排序,则需要做的是: function cmp(array $a, array $b) { return $a['baz'] - $b['baz']; } 多亏了数学运算的功率,它返回的值$a小于,等于0还是大于0,具体取决于是小于,等于还是大于$b。 请注意,这不适用于float值,因为它们会降低为int并失去精度。使用显式-1,0并1返回值。 对象 如果您有一个对象数组,则其工作方式相同: function cmp($a, $b) { return $a->baz - $b->baz; } 功能 您可以在比较函数内做任何需要的事情,包括调用函数: function cmp(array $a, array $b) { return someFunction($a['baz']) - someFunction($b['baz']); } 弦乐 第一个字符串比较版本的快捷方式: function cmp(array $a, array $b) { return strcmp($a['foo'], $b['foo']); } strcmp不正是要的是什么的cmp在这里,它返回-1,0或1。 飞船操作员 PHP 7引入了spaceship运算符,该运算符统一并简化了与跨类型比较相比相等/较小/​​较大的操作: function cmp(array $a, array $b) { return $a['foo'] <=> $b['foo']; } 按多个字段排序 如果要主要按排序foo,但如果foo两个元素相等,则按baz: function cmp(array $a, array $b) { if (($cmp = strcmp($a['foo'], $b['foo'])) !== 0) { return $cmp; } else { return $a['baz'] - $b['baz']; } } 对于熟悉的人,这等效于带有的SQL查询ORDER BY foo, baz。 另请参见此非常简洁的速记版本,以及如何为任意数量的键动态创建此类比较功能。 整理成手动,静态订单 如果要将元素按“手动顺序”排序,例如“ foo”,“ bar”,“ baz”: function cmp(array $a, array $b) { static $order = array('foo', 'bar', 'baz'); return array_search($a['foo'], $order) - array_search($b['foo'], $order); } 对于上述所有情况,如果您使用的是PHP 5.3或更高版本(确实应该使用),请对较短的代码使用匿名函数,并避免使另一个全局函数随处可见: usort($array, function (array $a, array $b) { return $a['baz'] - $b['baz']; }); 这就是对复杂的多维数组进行排序的简单方式。再次,只考虑在教授PHP时如何分辨两个项目中的哪个“更大”;让PHP进行实际排序。 同样对于以上所有内容,要在升序和降序之间切换,只需交换$a和$b参数即可。例如: return $a['baz'] - $b['baz']; // ascending return $b['baz'] - $a['baz']; // descending 根据另一数组对一个数组进行排序 然后是奇特的array_multisort,可让您根据另一个数组对一个数组进行排序: $array1 = array( 4, 6, 1); $array2 = array('a', 'b', 'c'); 这里的预期结果将是: $array2 = array('c', 'a', 'b'); // the sorted order of $array1 使用array_multisort到那里: array_multisort($array1, $array2); 从PHP 5.5.0开始,您可以array_column从多维数组中提取一列并对该列进行排序: array_multisort(array_column($array, 'foo'), SORT_DESC, $array); 从PHP 7.0.0开始,您还可以从对象数组中提取属性。 问题来源于stack overflow

保持可爱mmm 2020-01-16 14:54:07 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站