凯发k8国际娱乐官网入口-k8凯发> 对象存储服务 obs> > > > 基于浏览器上传的表单中携带签名
更新时间:2024-01-16 gmt 08:00

基于浏览器上传的表单中携带签名-凯发k8国际娱乐官网入口

obsk8凯发的服务支持基于浏览器的post上传对象请求,此类请求的签名信息通过表单的方式上传。post上传对象:首先,创建一个安全策略,指定请求中需要满足的条件,比如:桶名、对象名前缀;然后,创建一个基于此策略的签名,需要签名的请求表单中必须包含有效的signature和policy;最后,创建一个表单将对象上传到桶中。

签名的计算过程如下:

  1. 对policy内容进行utf-8编码。
  2. 对第一步的结果进行base64编码。
  3. 使用sk对第二步的结果进行hmac-sha1签名计算。
  4. 对第三步的结果进行base64编码,得到签名。
stringtosign = base64( utf-8-encoding-of( policy ) )
signature = base64( hmac-sha1( yoursecretaccesskeyid, stringtosign ) )
policy的内容如下:
{ "expiration": "2017-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

描述本次签名的有效时间iso 8601 utc,如实例中"expiration": "2017-12-31t12:00:00.000z"表示请求在2017年12月31日12点之后无效。该字段是policy中必选字段。合法格式仅有"yyyy-mm-dd't'hh:mm:ss'z'"和"yyyy-mm-dd't'hh:mm:ss.sss'z'"。

conditions

conditions是一个用于验证本次请求合法的一种机制,可以使用这些条件限制请求中必须包含的内容。实例中的条件要求请求的桶名必须是book,对象名必须以user/为前缀,对象的acl必须是公共可读。除了accesskeyid、signature、file、policy、token、field names以及前缀为x-ignore-外的表单中的所有项,都需要包含在policy中。下表是conditions中应该包含的项:

表1 policy中应该包含的条件元素

元素名称

描述

x-obs-acl

请求中的acl。

支持精确匹配和starts-with条件匹配。

content-length-range

设置上传对象的最大最小长度,支持range匹配。

cache-control, content-type, content-disposition, content-encoding, expires

rest请求特定头域。

支持精确匹配和starts-with条件匹配。

key

上传对象的名字。

支持精确匹配和starts-with条件匹配。

bucket

请求桶名。

支持精确匹配。

success_action_redirect

上传对象成功后重定向的url地址。具体描述请参见5.4.2-post上传

支持精确匹配和starts-with条件匹配。

success_action_status

如果未指定success_action_redirect,则成功上传时返回给客户端的状态码。具体描述请参见5.4.2-post上传

支持精确匹配。

x-obs-meta-*

用户自定义元数据。

元素中的关键字不允许含有非ascii码或不可识别字符,如果一定要使用非ascii码或不可识别字符,需要客户端自行做编解码处理,可以采用url编码或者base64编码,服务端不会做解码处理。

支持精确匹配和starts-with条件匹配。

x-obs-*

其他以x-obs-为前缀的头域。

支持精确匹配和starts-with条件匹配。

x-obs-security-token

请求消息头中字段名。

临时ak/sk和securitytoken鉴权必加字段名。

policy条件匹配的方式如下:

表2 policy条件匹配方式

条件

描述

exact matches

默认是完全匹配,post表单中该项的值必须和policy的conditions中设置的值完全一样。例如:上传对象的同时设置对象acl为public-read,表单中x-obs-acl元素的值为public-read,policy中的conditions可以设置为

{"x-obs-acl": "public-read" }或者[ "eq", "$x-obs-acl", "public-read"],这两者是等效的。

starts with

如果使用该条件,则post表单中对应元素的值必须是固定字符串开始。例如:上传对象名以user/为前缀,表单中key元素的值可以是user/test1、user/test2,policy的conditions中该条件如下:

["starts-with", "$key", "user/"]

matching any content

post表单中对应元素的值可以是任意值。例如:请求成功后重定向的地址可以是任意地址,表单中success_action_redirect元素的值可以是任意值,policy的conditions中该条件如下:

["starts-with", "$success_action_redirect", ""]

specifying ranges

post表单中file元素文件的内容长度可以是一个指定的范围,只用于限制对象大小。例如上传对象大小为1-10mb,表单中file元素的内容长度可以是1048576-10485760,policy的conditions中该条件如下,注意值没有双引号:

["content-length-range", 1048576, 10485760]

policy使用json格式,conditions可以支持 { } 和 [ ] 两种方式,{ }中包含表单元素的key和value两项,以冒号分隔;[ ]中包含条件类型、key、value三项,以逗号分隔,元素key之前使用$字符表示变量。

policy中必须转义的字符如下:

表3 policy中必须转义的字符

转义后的字符

真实字符

\\

反斜杠(\)

\$

美元符号($)

\b

退格

\f

换页

\n

换行

\r

回车

\t

水平制表

\v

垂直制表

\uxxxx

所有unicode字符

请求和policy示例

下面的几张表提供了一些请求和policy的例子。

示例1:在examplebucket桶中上传testfile.txt对象,并且设置对象acl为公共可读

请求

policy

post / http/1.1

host: examplebucket.obs.cn-north-4.myhuaweicloud.com

content-type: multipart/form-data; boundary=7e32233530b26

content-length: 1250

--7e32233530b26

content-disposition: form-data; name="key"

testfile.txt

--7e32233530b26

content-disposition: form-data; name="x-obs-acl"

public-read

--7e32233530b26

content-disposition: form-data; name="content-type"

text/plain

--7e32233530b26

content-disposition: form-data; name="accesskeyid"

udsiamstubtest000002

--7e32233530b26

content-disposition: form-data; name="policy"

ewogicjlehbpcmf0aw9uijogijiwmtktmdctmdfumti6mda6mdaumdawwiisciagimnvbmrpdglvbnmioibbciagicb7imj1y2tldci6icjlegftcgxlynvja2v0iib9laogicagwyjlcsisicika2v5iiwginrlc3rmawxllnr4dcjdlaojeyj4lw9icy1hy2wioiaichvibgljlxjlywqiih0sciagicbbimvxiiwgiirdb250zw50lvr5cguilcaidgv4dc9wbgfpbijdlaogicagwyjjb250zw50lwxlbmd0ac1yyw5nzsisidysidewxqogif0kfqo=

--7e32233530b26

content-disposition: form-data; name="signature"

xxl7bzs/5fgtbuggodq88dpzuo0=

--7e32233530b26

content-disposition: form-data; name="file"; filename="e:\test_file\test.txt"

content-type: text/plain

123456

--7e32233530b26

content-disposition: form-data; name="submit"

upload

--7e32233530b26--

{

"expiration": "2019-07-01t12:00:00.000z",

"conditions": [

{"bucket": "examplebucket" },

["eq", "$key", "testfile.txt"],

{"x-obs-acl": "public-read" },

["eq", "$content-type", "text/plain"]

]

}

示例2:在examplebucket桶中上传file/obj1对象,并且设置对象的四个自定义元数据

请求

policy

post / http/1.1

host: examplebucket.obs.cn-north-4.myhuaweicloud.com

content-type: multipart/form-data; boundary=7e329d630b26

content-length: 1597

--7e3542930b26

content-disposition: form-data; name="key"

file/obj1

--7e3542930b26

content-disposition: form-data; name="accesskeyid"

udsiamstubtest000002

--7e3542930b26

content-disposition: form-data; name="policy"

ewogicjlehbpcmf0aw9uijogijiwmtktmdctmdfumti6mda6mdaumdawwiisciagimnvbmrpdglvbnmioibbciagicb7imj1y2tldci6icjlegftcgxlynvja2v0iib9laogicagwyjzdgfydhmtd2l0acisicika2v5iiwgimzpbguvil0sciagicb7ingtb2jzlw1ldgetdgvzddeioij2ywx1zteifswkicagifsizxeilcaijhgtb2jzlw1ldgetdgvzddiilcaidmfsdwuyil0sciagicbbinn0yxj0cy13axroiiwgiir4lw9icy1tzxrhlxrlc3qziiwgimrvyyjdlaogicagwyjzdgfydhmtd2l0acisicikec1vynmtbwv0ys10zxn0ncisiciixqogif0kfqo=

--7e3542930b26

content-disposition: form-data; name="signature"

htid8hcaisn6ffdwkqsjp9rn4oo=

--7e3542930b26

content-disposition: form-data; name="x-obs-meta-test1"

value1

--7e3542930b26

content-disposition: form-data; name="x-obs-meta-test2"

value2

--7e3542930b26

content-disposition: form-data; name="x-obs-meta-test3"

doc123

--7e3542930b26

content-disposition: form-data; name="x-obs-meta-test4"

my

--7e3542930b26

content-disposition: form-data; name="file"; filename="e:\test_file\test.txt"

content-type: text/plain

123456

--7e3542930b26

content-disposition: form-data; name="submit"

upload

--7e3542930b26--

{

"expiration": "2019-07-01t12:00:00.000z",

"conditions": [

{"bucket": "examplebucket" },

["starts-with", "$key", "file/"],

{"x-obs-meta-test1":"value1"},

["eq", "$x-obs-meta-test2", "value2"],

["starts-with", "$x-obs-meta-test3", "doc"],

["starts-with", "$x-obs-meta-test4", ""]

]

}

java中签名的计算方法为:

 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
82
83
84
85
86
87
88
import java.text.simpledateformat;
import java.util.arraylist;
import java.util.base64;
import java.util.collections;
import java.util.date;
import java.util.list;
import java.util.timezone;
import javax.crypto.mac;
import javax.crypto.spec.secretkeyspec;
public class signdemo {
    private static final string default_encoding = "utf-8";
    private static final string expiration_date_formatter = "yyyy-mm-dd't'hh:mm:ss.sss'z'";
    private static final timezone gmt_timezone = timezone.gettimezone("gmt");
    private static final long default_expire_seconds = 300;
    private string ak;
    private string sk;
    private string join(list items) {
        stringbuilder sb = new stringbuilder();
        for (int i = 0; i < items.size(); i) {
            string item = items.get(i).tostring();
            sb.append(item);
            if (i < items.size() - 1) {
                sb.append(",");
            }
        }
        return sb.tostring();
    }
    private string stringtosign(string[] tmpconditions, string expiration) {
        list<string> conditions = new arraylist<>();
        collections.addall(conditions, tmpconditions);
        return "{\"expiration\":"  "\""  expiration  "\","  "\"conditions\":["  join(conditions)  "]}";
    }
    private string getformatexpiration(date requestdate, long expires) {
        requestdate = requestdate != null ? requestdate : new date();
        simpledateformat expirationdateformat = new simpledateformat(expiration_date_formatter);
        expirationdateformat.settimezone(gmt_timezone);
        date expirydate = new date(requestdate.gettime()  (expires <= 0 ? default_expire_seconds : expires) * 1000);
        return expirationdateformat.format(expirydate);
    }
    public string postsignature(string policy) throws exception {
        byte[] policybase64 = base64.getencoder().encode(policy.getbytes(default_encoding));
        secretkeyspec signingkey = new secretkeyspec(this.sk.getbytes(default_encoding), "hmacsha1");
        mac mac = mac.getinstance("hmacsha1");
        mac.init(signingkey);
        return base64.getencoder().encodetostring(mac.dofinal(policybase64));
    }
    public static void main(string[] args) throws exception {
        signdemo demo = new signdemo();
        /* 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
        本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量huaweicloud_sdk_ak和huaweicloud_sdk_sk。*/
        demo.ak = system.getenv("huaweicloud_sdk_ak");
        demo.sk = system.getenv("huaweicloud_sdk_sk");
        string authexpiration = demo.getformatexpiration(null, 0);
        string[] tmpconditions = {
            "{\"bucket\": \"bucketname\" }",
            "[\"starts-with\", \"$key\", \"obj\"]"
        };
        string policy = demo.stringtosign(tmpconditions, authexpiration);
        string policybase64 = base64.getencoder().encodetostring(policy.getbytes(default_encoding));
        string signature = 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);
    }
}

python中签名的计算方法为:

# 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是timestamp形式 e.g. 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)
分享:
网站地图