华为云对象存储OBS基于浏览器上传的表单中携带签名_云淘科技

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

签名的计算过程如下:

对policy内容进行UTF-8编码。
对第一步的结果进行Base64编码。
使用SK对第二步的结果进行HMAC-SHA1签名计算。
对第三步的结果进行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

换页

换行

\r

回车

水平制表

\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
89
90
91
92
93
94
95
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
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, String delimiter) {
        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(delimiter);
            }
        }
        return sb.toString();
    }

    public String hmacSha1(String input)
        throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        SecretKeySpec signingKey = new SecretKeySpec(this.sk.getBytes(DEFAULT_ENCODING), "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);
        return Base64.getEncoder()
            .encodeToString(mac.doFinal(Base64.getEncoder().encode(input.getBytes(DEFAULT_ENCODING))));
    }

    private String stringToSign(List conditions, String expiration) {
        StringBuilder policy = new StringBuilder();
        policy.append("{\"expiration\":").append("\"").append(expiration).append("\",")
            .append("\"conditions\":[");

        policy.append(join(conditions, ","));
        policy.append("]}");
        return policy.toString();
    }

    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);
        String expiration = expirationDateFormat.format(expiryDate);
        return expiration;
    }

    public String postSignature(List conditions, String expiration) throws Exception {
        return hmacSha1(this.stringToSign(conditions, expiration));
    }

    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 expiration = demo.getFormatExpiration(null, 0);
        List conditions = new ArrayList();
        String[] tmpConditions = {
            "{\"x-obs-acl\": \"public-read\" }",
            "{\"x-obs-security-token\": \"YwkaRTbdY8g7q....\" }",
            "{\"bucket\": \"book\" }",
            "[\"starts-with\", \"$key\", \"user/\"]"
        };
        for (String condition : tmpConditions) {
            conditions.add(condition);
        }

        System.out.println(expiration);
        System.out.println(demo.postSignature(conditions, expiration));
    }
}

Python中签名的计算方法为:

 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
# coding=utf-8
import hashlib
import hmac
import time
import pytz
import binascii

from datetime import datetime


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

    def set_secret_access_key(self, sk):
        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 hmac_sha1(self, ipt):
        # 如果使用binascii或encode("base64"), 需要去除换行符
        policy_base64 = binascii.b2a_base64(ipt.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()

    def post_signature(self, conditions, expiration):
        return self.hmac_sha1(self.string_to_sign(conditions, expiration))

    @staticmethod
    def string_to_sign(conditions, expiration):
        policy = ""
        policy += "{\"expiration\":" + "\"" + expiration + "\"," + "\"conditions\":["
        policy += ",".join(conditions) + "]}"
        return policy


if __name__ == "__main__":
    demo = SignatureDemo()

    # 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
    # 本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。
    demo.ak = os.getenv('HUAWEICLOUD_SDK_AK')
    demo.set_secret_access_key(os.getenv('HUAWEICLOUD_SDK_SK'))

    exp = demo.get_format_expiration(None, 0)
    conditions_example = [
        "{\"x-obs-acl\": \"public-read\" }",
        "{\"x-obs-security-token\": \"YwkaRTbdY8g7q....\" }",
        "{\"bucket\": \"book\" }",
        "[\"starts-with\", \"$key\", \"user/\"]"
    ]
    print(exp)
    print(demo.post_signature(conditions_example, exp))

父主题: 认证鉴权

同意关联代理商云淘科技,购买华为云产品更优惠(QQ 78315851)

内容没看懂? 不太想学习?想快速解决? 有偿解决: 联系专家