基于浏览器上传的表单中携带签名-九游平台
功能介绍
obs九游平台的服务支持基于浏览器的post上传对象请求,此类请求的签名信息通过表单的方式上传。计算post表单上传请求的签名,需要先定义一个安全策略(policy),这个安全策略的作用是限制表单上传的内容,例如规定表单上传对象的对象名前缀必须以“prefix01”开头,使用policy能够帮助您更好的管控桶中的文件。post上传对象的流程是:
- 创建一个policy,指定请求中需要满足的条件,比如:桶名、对象名前缀。
- 计算基于此policy的签名。
- 创建一个表单,表单中必须包含有效的签名和policy,使用该表单将对象上传到桶中。
步骤一:创建policy
下面将以如下policy为例介绍policy的组成和语法。下方的policy限制了签名的有效时间为2024年12月31日12点之后无效,上传对象的访问权限限制为公共读,请求消息头中的x-obs-security-token须等于"ywkartbdy8g7q...." },对象上传的桶名需要为“book”,对象名必须以前缀“user/”开头:
1 2 3 4 5 6 7 8 |
{"expiration":"2024-12-31t12:00:00.000z", "conditions":[ {"x-obs-acl":"public-read"}, {"x-obs-security-token":"ywkartbdy8g7q...."}, {"bucket":"book"}, ["starts-with","$key","user/"] ] } |
policy策略中由有效时间有效时间:expiration和条件元素条件元素:conditions两部分组成。
有效时间:expiration
参数名称 |
参数类型 |
是否必选 |
描述 |
---|---|---|---|
expiration |
string |
必选 |
参数解释: 描述本次签名的有效时间。如示例中"expiration": "2024-12-31t12:00:00.000z"表示请求在2024年12月31日12点之后无效。 约束限制: 格式为iso 8601 utc,"yyyy-mm-dd't'hh:mm:ss'z'"或"yyyy-mm-dd't'hh:mm:ss.sss'z'"。 取值范围: 不涉及 默认取值: 无 |
条件元素:conditions
使用conditions可以要求调用请求必须满足指定的条件限制。示例中的条件要求请求的桶名必须是book,对象名必须以user/为前缀,对象的acl必须是公共可读。policy可以限制除了accesskeyid、signature、file、policy、token、field names以及前缀为"x-ignore-"外的表单中的其他所有项。下表是conditions中可校验的元素:
元素名称 |
元素类型 |
描述 |
支持的匹配方式 |
||
---|---|---|---|---|---|
x-obs-acl |
string |
请求中的acl。 |
精确匹配:exact matches 前缀匹配:starts-with |
||
content-length-range |
int |
设置上传对象的最大和最小长度。例如:
|
范围匹配:specifying ranges |
||
cache-control, content-type, content-disposition, content-encoding, expires |
string |
rest请求特定头域。 |
精确匹配:exact matches 前缀匹配:starts-with |
||
key |
string |
上传对象的名字。 |
精确匹配:exact matches 前缀匹配:starts-with |
||
bucket |
string |
请求桶名。 |
精确匹配:exact matches |
||
success_action_redirect |
string |
上传对象成功后重定向的url地址。具体描述请参见5.4.2-post上传。 |
精确匹配:exact matches 前缀匹配:starts-with |
||
success_action_status |
string |
如果未指定success_action_redirect,则成功上传时返回给客户端的状态码。具体描述请参见5.4.2-post上传。 |
精确匹配:exact matches |
||
x-obs-meta-* |
string |
用户自定义元数据。 元素中的关键字不允许含有非ascii码或不可识别字符,如果一定要使用非ascii码或不可识别字符,需要客户端自行做编解码处理,可以采用url编码或者base64编码,服务端不会做解码处理。 |
精确匹配:exact matches 前缀匹配:starts-with |
||
x-obs-* |
string |
其他以x-obs-为前缀的头域。 |
精确匹配:exact matches 前缀匹配:starts-with |
||
x-obs-security-token |
string |
请求消息头中字段名。 临时ak/sk和securitytoken鉴权必加字段名。如何获取临时ak/sk和securitytoken请参考。 |
精确匹配:exact matches |
policy条件匹配的方式如下:
匹配方式 |
描述 |
||||
---|---|---|---|---|---|
精确匹配(exact matches) |
默认是完全匹配,post表单中该项的值必须和policy的conditions中设置的值完全一样。例如:上传对象的同时设置对象acl为public-read,表单中x-obs-acl元素的值为public-read,policy中的conditions可以设置为
或者另一种等效的格式:
|
||||
前缀匹配(starts with) |
如果使用该条件,则post表单中对应元素的值必须是固定字符串开始。例如:上传对象名以user/为前缀,表单中key元素的值可以是user/test1、user/test2,policy的conditions中该条件如下:
|
||||
任意匹配(matching any content) |
post表单中对应元素的值可以是任意值。例如:请求成功后重定向的地址可以是任意地址,表单中success_action_redirect元素的值可以是任意值,policy的conditions中该条件如下:
|
||||
范围匹配(specifying ranges) |
post表单中file元素文件的内容长度可以是一个指定的范围,只用于限制对象大小。例如上传对象大小为1-10mb,表单中file元素的内容长度可以是1048576-10485760,policy的conditions中该条件如下,注意值没有双引号:
|
policy使用json格式,conditions可以支持 { } 和 [ ] 两种方式,{ }中包含表单元素的key和value两项,以冒号分隔;[ ]中包含条件类型、key、value三项,以逗号分隔,元素key之前使用$字符表示变量。
policy中必须转义的字符如下:
转义后的字符 |
真实字符 |
---|---|
\\ |
反斜杠(\) |
\$ |
美元符号($) |
\b |
退格 |
\f |
换页 |
\n |
换行 |
\r |
回车 |
\t |
水平制表 |
\v |
垂直制表 |
\uxxxx |
所有unicode字符 |
步骤二:计算签名
obs有2种计算表单中携带签名的方式:
sdk签名实现
sdk |
签名实现源文件 |
---|---|
java |
|
python |
|
go |
|
c |
- |
nodejs |
|
browserjs |
|
php |
|
.net |
- |
签名算法
表单中携带签名的计算公式如下:
signature = base64( hmac-sha1( yoursecretaccesskeyid, stringtosign ) ) stringtosign = base64( utf-8-encoding-of( policy ) )
签名的计算过程如下:
- 构造请求字符串stringtosign,即对policy先进行utf8编码,然后再进行base64编码。
- 使用sk第一步结果进行hmac-sha1签名计算。
- 对第二步的结果进行base64编码,得到签名。
签名代码示例
以下是计算表单中携带签名的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
importjava.text.simpledateformat;↵ importjava.util.arraylist;↵ importjava.util.base64;↵ importjava.util.collections;↵ importjava.util.date;↵ importjava.util.list;↵ importjava.util.timezone;↵ ↵ importjavax.crypto.mac;↵ importjavax.crypto.spec.secretkeyspec;↵ ↵ publicclass signdemo{↵ ↵ privatestaticfinalstringdefault_encoding="utf-8";↵ privatestaticfinalstringexpiration_date_formatter="yyyy-mm-dd't'hh:mm:ss.sss'z'";↵ privatestaticfinaltimezonegmt_timezone=timezone.gettimezone("gmt");↵ privatestaticfinallongdefault_expire_seconds=300;↵ ↵ privatestringak;↵ privatestringsk;↵ ↵ privatestringjoin(listitems){↵ stringbuildersb=newstringbuilder();↵ for(inti=0;i<items.size();i){ stringitem=items.get(i).tostring();sb.append(item); if(i<items.size()-1){ sb.append(","); } } returnsb.tostring(); } // 构造stringtosign privatestringstringtosign(string[]tmpconditions,stringexpiration){ list↵conditions=newarraylist<>(); collections.addall(conditions,tmpconditions); return"{\"expiration\":""\""expiration"\",""\"conditions\":["join(conditions)"]}"; } privatestringgetformatexpiration(daterequestdate,longexpires){ requestdate=requestdate!=null?requestdate:newdate(); simpledateformatexpirationdateformat=newsimpledateformat(expiration_date_formatter); expirationdateformat.settimezone(gmt_timezone); dateexpirydate=newdate(requestdate.gettime()(expires<=0?default_expire_seconds:expires)*1000); returnexpirationdateformat.format(expirydate); } // 计算签名 publicstringpostsignature(stringpolicy)throwsexception{ byte[]policybase64=base64.getencoder().encode(policy.getbytes(default_encoding)); secretkeyspecsigningkey=newsecretkeyspec(this.sk.getbytes(default_encoding),"hmacsha1"); macmac=mac.getinstance("hmacsha1");mac.init(signingkey); returnbase64.getencoder().encodetostring(mac.dofinal(policybase64)); } publicstaticvoidmain(string[]args)throwsexception{ signdemodemo=newsigndemo(); /* 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全; 本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量huaweicloud_sdk_ak和huaweicloud_sdk_sk。*/ demo.ak=system.getenv("huaweicloud_sdk_ak"); demo.sk=system.getenv("huaweicloud_sdk_sk"); stringauthexpiration=demo.getformatexpiration(null,0); string[]tmpconditions={"{\"bucket\": \"bucketname\" }","[\"starts-with\", \"$key\", \"obj\"]"}; stringpolicy=demo.stringtosign(tmpconditions,authexpiration); stringpolicybase64=base64.getencoder().encodetostring(policy.getbytes(default_encoding)); stringsignature=demo.postsignature(policy); // 表单中携带accesskeyid、policy、signature的签名 system.out.println("authexpiration="authexpiration); system.out.println("policy="policy); system.out.println("policybase64="policybase64); system.out.println("signature="signature); // 表单中携带token的签名 system.out.println("token="demo.ak":"signature":"policybase64); } }↵ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# coding=utf-8 import binascii import hashlib import hmac import os import time from datetime import datetime import pytz class signaturedemo: expiration_date_formatter = "%y-%m-%dt%h:%m:%s.%f" default_encoding = "utf-8" # 默认过期时间五分钟 default_expire_seconds = 300 gmt_timezone = "gmt" def __init__(self, ak=none, sk=none): self.ak = ak self.sk = sk # request_date和expires是时间戳形式,例如:1675651495.979 def get_format_expiration(self, request_date, expires): request_date = request_date if request_date else time.time() expiry_date = request_date (expires if expires > 0 else self.default_expire_seconds) expiration = datetime.fromtimestamp(expiry_date, pytz.timezone(self.gmt_timezone)).strftime( self.expiration_date_formatter)[:-3] "z" return expiration def post_signature(self, policy): # 如果使用binascii或encode("base64"), 需要去除换行符 policy_base64 = binascii.b2a_base64(policy.encode(self.default_encoding)).rstrip() hashed = hmac.new(self.sk.encode(self.default_encoding), policy_base64, hashlib.sha1) return binascii.b2a_base64(hashed.digest()).rstrip() @staticmethod def string_to_sign(conditions, expiration): return "{\"expiration\":" "\"" expiration "\"," "\"conditions\":[" ",".join(conditions) "]}" if __name__ == "__main__": demo = signaturedemo() # 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全; # 本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量huaweicloud_sdk_ak和huaweicloud_sdk_sk。 demo.ak = os.getenv('huaweicloud_sdk_ak') demo.sk = os.getenv('huaweicloud_sdk_sk') auth_expiration = demo.get_format_expiration(none, 0) conditions_example = [ "{\"bucket\": \"bucketname\" }", "[\"starts-with\", \"$key\", \"obj\"]" ] post_policy = demo.string_to_sign(conditions_example, auth_expiration) policy_base64 = binascii.b2a_base64(post_policy.encode(demo.default_encoding)).rstrip() signature = demo.post_signature(post_policy) # 表单中携带accesskeyid、policy、signature的签名 print("authexpiration=" auth_expiration) print("policy=" post_policy) print("policybase64=" policy_base64) print("signature=" signature) # 表单中携带token的签名 print("token=" demo.ak ":" signature ":" policy_base64) |
相关文档
意见反馈
文档内容是否对您有帮助?
如您有其它疑问,您也可以通过华为云社区问答频道来与我们联系探讨