阿里云oss的Authorization算法解析

分析源代码为阿里云oss的php——sdk。

名词解释

一. Object

在OSS中,用户的每个文件都是一个Object,文件最大大小 根据上传数据方式不同而不同, Put Object方式最大不能超过5GB, 使用multipart上传方式object大小不能超过48.8TB。Object包含key、data和user meta。key是Object名,user meta是对数据的描述。
Object命名规范:

  1. 使用UTF-8编码
  2. 规则长度必须在1-1023字节之间

二. Bucket

存储在OSS上的每个Object必须都包含在Bucket中,Bucket名在整个OSS中具有全局唯一性,且不能修改。一个应用,例如图片分享网站,可以对应一个或多个Bucket。一个用户最多可创建10个Bucket,但每个Bucket中存放的Object的数量和大小总和没有限制,用户不需要考虑数据的可扩展性。
Bucket命名规范:

  1. 只能包括小写字母,数字和短横线(-)
  2. 必须以小写字母或者数字开头
  3. 长度必须在3-255字节之间

三. Service

Service对于某用户来说就是OSS提供给该用户的虚拟存储空间。用户可以在这个存储空间中拥有一个或者多个Bucket。

四. Access ID & Access Key (API密钥)

用户注册OSS时,系统会给用户分配一对Access ID和Access Key,称为ID对,用于标识用户,为访问OSS做签名验证。
当您通过oss.aliyun.com成功创建的OSS存储服务后,可以进入“管理中心” -“获取API 密钥” 得到Access ID & Access Key

五.signable resource

需要签名的资源

流程解析

以上传一个文件到oss来解析签名的生成过程。下面是上传文件的代码:

//通过路径上传文件
function upload_by_file($obj) {
    $bucket = 'zhimakaimen';
    $object = '/demo/tutorial.php';
    $file_path = "/home/caixin/Project/oss_test/demo/tutorial.php";
    $response = $obj->upload_file_by_file($bucket, $object, $file_path);
    _format($response);
}

在这里一共显示的传入了三个参数:bucketobjectfile_path。重点说下object,在这里object看似是一个文件路径,我们也可以把他理解成一个文件。但实质上这是一个要在oss上创建的一个key。和key对应的就是文件内容了,所以这个会做相应的处理。
. 资源签名
现在来开始做资源签名,如果没有指定object的时候是不需要做资源签名的。
1. 替换%2F%25
在这里需要把%2F%25分别替换为/%。并在前面增加一个/SDK中的代码是这样的。

$signable_resource = '/' . str_replace(array('%2F', '%25'), array('/', '%'), rawurlencode('/demo/tutorial.php'));

替换后:

string(19) "//demo/tutorial.php"

注意rawurlencode是把除了-_.的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数。编码是RFC 3986
这里没有做分块上传的,如果分块上传的话会有块iduploadid等参数组装。
2. 继续
先组装请求方法,这里是上传文件,自然是put。组装格式为+"\n"。示例:

$string_to_sign .= "PUT" . "\n";

headers进行自然排序。排序后的结果:

array(4) {
  ["Content-Md5"]=>
  string(0) ""
  ["Content-Type"]=>
  string(24) "application/octet-stream"
  ["Date"]=>
  string(29) "Wed, 19 Nov 2014 08:30:30 GMT"
  ["Host"]=>
  string(16) "oss.aliyuncs.com"
}

headers值中的\r\n替换为空格,如:

str_replace(array("\r", "\n"), '', $header_value);

继续组装,这里需要分别判断:
当headers为content-md5content-typedate或者preauth大于0,继续同上组装方法一样来组装数据。
只有当headers的前六个字符是x-oss-的时候,改变组装方式。组装方式如下:

headers的key+:+headers的值+"\n"

headers数据组装完后打印如下:

string(60) "PUT

application/octet-stream
Wed, 19 Nov 2014 08:57:44 GMT
"

注意这里看不到组装后的\n,是因为换行符已经在终端解析了。
组装bucket,代码如下:

$string_to_sign .= '/' . 'zhimakaimen';

把生成好的签名资源组装上。

$string_to_sign .= rawurldecode($signable_resource)

需要对签名资源进行RFC 3986转码,现在组装号的签名输入如下:

string(91) "PUT

application/octet-stream
Wed, 19 Nov 2014 09:10:02 GMT
/zhimakaimen//demo/tutorial.php"

使用 HMAC 方法生成带有密钥的哈希值,加密方式为sha1,密匙是ACCESS_KEY。输入原始二进制数据:

hash_hmac('sha1', $string_to_sign, '这里是key', true);

把这个值用MIME base64转码一遍,php函数为base64_encode
这是最终生成的签名数据:

string(28) "NiwMZLv4K/WfhXTcKPEXZiYljrU="

签名数据生成完毕,添加到请求的headers中,headerskeyAuthorization,值的组装如下:

'OSS ' . '这里是access_id' . ':' . $signature。

整个生成流程就是这样,可以发送这个请求了。

标签: php, authorization, oss

添加新评论