
# 基于浏览器上传的表单中携带签名
#### 功能介绍
OBS服务支持基于浏览器的POST上传对象请求，此类请求的签名信息通过表单的方式上传。计算POST表单上传请求的签名，需要先定义一个安全策略（policy），这个安全策略的作用是限制表单上传的内容，例如规定表单上传对象的对象名前缀必须以"prefix01"开头，使用policy能够帮助您更好的管控桶中的文件。POST上传对象的流程是：
1. 创建一个policy，指定请求中需要满足的条件，比如：桶名、对象名前缀。
2. 计算基于此policy的签名。
3. 创建一个表单，表单中必须包含有效的签名和policy，使用该表单将对象上传到桶中。
 
#### 步骤一：创建policy
下面将以如下policy为例介绍policy的组成和语法。下方的policy限制了签名的有效时间为2024年12月31日12点（UTC时间）之后无效，上传对象的访问权限限制为公共读，请求消息头中的x-obs-security-token须等于"YwkaRTbdY8g7q...." }，对象上传的桶名需要为"book"，对象名必须以前缀"user/"开头：
```
{ "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
表1有效时间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、前缀为"x-ignore-"的字段外的其他所有表单项。下表是conditions中可校验的元素：
表2policy中可以包含的条件元素 
| **元素名称**                                                                    | **元素类型** | **描述**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | **支持的匹配方式**                                                                         |
|:---|:---|:---|:---|
| x-obs-acl                                                                   | String   | **参数解释：** 创建对象时，可以加上此元素设置对象的权限控制策略，使用的策略为预定义的常用策略。 **约束限制**： 预定义策略需要以字符串形式呈现。 **取值范围：** - private：私有  - public-read：公共读  - public-read-write：公共读写   各策略详细说明见[ACL](https://support.huaweicloud.com/perms-cfg-obs/obs_40_0005.html)章节的"使用头域设置ACL"。 **默认取值：** private                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | 精确匹配：Exact Matches 前缀匹配：starts-with |
| content-length-range                                                        | int\[\]  | 设置上传对象的最大和最小长度。例如： ``` ["content-length-range", 1048576, 10485760] ```                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | 范围匹配：Specifying Ranges                                                              |
| Cache-Control, Content-Type, Content-Disposition, Content-Encoding, Expires | String   | REST请求特定头域。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | 精确匹配：Exact Matches 前缀匹配：starts-with    |
| key                                                                         | String   | **参数解释：** 上传对象的名字。对象名是对象在存储桶中的唯一标识。对象名是对象在桶中的完整路径，路径中不包含桶名。 例如，您对象的访问地址为examplebucket.obs.cn-north-4.myhuaweicloud.com/folder/test.txt 中，对象名为folder/test.txt。 **取值范围：** 长度大于0且不超过1024的字符串。 **默认取值：** 无                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | 精确匹配：Exact Matches 前缀匹配：starts-with   |
| bucket                                                                      | String   | **参数解释** **：** 请求桶名。 **约束限制：** - 桶的名字需全局唯一，不能与已有的任何桶名称重复，包括其他用户创建的桶。  - 桶命名规则如下： - 3～63个字符，数字或字母开头，支持小写字母、数字、"-"、"."。  - 禁止使用IP地址。  - 禁止以"-"或"."开头及结尾。  - 禁止两个"."相邻（如："my..bucket"）。  - 禁止"."和"-"相邻（如："my-.bucket"和"my.-bucket"）。    - 同一用户在同一个区域多次创建同名桶不会报错，创建的桶属性以第一次请求为准。   **默认取值：** 无 须知： 如果policy中包含"key"条件元素，则必须包含"bucket"条件元素。 | 精确匹配：Exact Matches                                                                  |
| success_action_redirect                                                     | String   | 上传对象成功后重定向的URL地址。具体描述请参见[5.4.2-POST上传](https://support.huaweicloud.com/api-obs/obs_04_0081.html)。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | 精确匹配：Exact Matches 前缀匹配：starts-with  |
| success_action_status                                                       | String   | 如果未指定success_action_redirect，则成功上传时返回给客户端的状态码。具体描述请参见[5.4.2-POST上传](https://support.huaweicloud.com/api-obs/obs_04_0081.html)。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | 精确匹配：Exact Matches                                                                  |
| x-obs-meta-\*                                                               | String   | **参数解释：** 创建对象时，可以在HTTP请求中加入以"x-obs-meta-"开头的消息头，用来加入自定义的元数据，以便对对象进行自定义管理。当用户获取此对象或查询此对象元数据时，加入的自定义元数据将会在返回消息的头中出现。更多说明详见[对象自定义元数据介绍](https://support.huaweicloud.com/usermanual-obs/obs_03_0324.html)。 示例：x-obs-meta-test: test metadata **约束限制：** 自定义元数据key-value对都**必须符合** **US-ASCII**。 如果一定要使用非ASCII码或不可识别字符，需要客户端**自行做编解码处理**，可以采用URL编码或者Base64编码，服务端不会做解码处理。 **取值范围：** 无 **默认取值：** 无                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | 精确匹配：Exact Matches 前缀匹配：starts-with |
| x-obs-\*                                                                    | String   | 其他以x-obs-为前缀的头域。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | 精确匹配：Exact Matches 前缀匹配：starts-with |
| x-obs-security-token                                                        | String   | **参数解释：** 使用临时AK/SK鉴权时，临时AK/SK和securitytoken必须同时使用，请求头中需要添加"x-obs-security-token"字段。 **约束限制：** 使用临时AK/SK和securitytoken鉴权时，本字段必选。 **取值范围：** 获取临时AK/SK和securitytoken参考[获取临时AK/SK和securitytoken](https://support.huaweicloud.com/api-iam/iam_04_0002.html)。 **默认取值：** 无                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | 精确匹配：Exact Matches                                                                  |
   
Policy条件匹配的方式如下：
表3policy条件匹配方式 
| **匹配方式**                   | **描述**                                                                                                                                                                                                                                                                                                                           |
|:---|:---|
| 精确匹配（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] ```                                                                                                 |
   
![](https://support.huaweicloud.com/api-obs/public_sys-resources/note_3.0-zh-cn.png)
policy使用json格式，conditions可以支持 { } 和 \[ \] 两种方式，{ }中包含表单元素的key和value两项，以冒号分隔；\[ \]中包含条件类型、key、value三项，以逗号分隔，元素key之前使用$字符表示变量。
Policy中必须转义的字符如下：
表4policy中必须转义的字符 
| **转义后的字符**    | **真实字符**    |
|:---|:---|
| \\\\          | 反斜杠(\\)     |
| \\$           | 美元符号($)     |
| \\b           | 退格          |
| \\f           | 换页          |
| \\n           | 换行          |
| \\r           | 回车          |
| \\t           | 水平制表        |
| \\v           | 垂直制表        |
| \\u***xxxx*** | 所有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", ""\] \] }  |
   
#### 步骤二：计算签名
OBS有2种计算表单中携带签名的方式：
表5计算表单签名的2种方式 
| **计算方式** | **描述**                                                   | **更多参考**                                                       |
|:---|:---|:---|
| SDK      | OBS所有语言的SDK都已实现表单上传签名，无需手动进行签名计算。推荐您直接使用SDK进行接口调用，更方便快捷。 | [SDK签名实现] |
| 手动编码计算签名 | 按照签名算法手动编码计算签名。                                          | [签名算法]    |
   
 #### SDK签名实现
表6OBS SDK Header携带签名的实现 
| SDK        | 签名实现源文件                                                                                                                                                                                  |
|:---|:---|
| Java       | [AbstractClient.java](https://github.com/huaweicloud/huaweicloud-sdk-java-obs/blob/5af1b31a130417b28bfab1579e5d7211e3f84dae/app/src/main/java/com/obs/services/AbstractClient.java#L472) |
| Python     | [client.py](https://github.com/huaweicloud/huaweicloud-sdk-python-obs/blob/master/src/obs/client.py#L1167)                                                                               |
| Go         | [temporary_other.go](https://github.com/huaweicloud/huaweicloud-sdk-go-obs/blob/5105f9c820b8f6a2017da873a9b283bd4e95f5ea/obs/temporary_other.go#L34)                                     |
| C          | -                                                                                                                                                                                        |
| Node.Js    | [utils.js](https://github.com/huaweicloud/huaweicloud-sdk-nodejs-obs/blob/master/lib/utils.js#L2269)                                                                                     |
| Browser.Js | [utils.js](https://github.com/huaweicloud/huaweicloud-sdk-browserjs-obs/blob/master/src/utils.js#L2469)                                                                                  |
| PHP        | [SendRequestTrait.php](https://github.com/huaweicloud/huaweicloud-sdk-php-obs/blob/51272ef0c4415607f0f68460ae7706be330b8a82/Obs/Internal/SendRequestTrait.php#L337)                      |
| .NET       | -                                                                                                                                                                                        |
   
 #### 签名算法
表单中携带签名的计算公式如下：
```
Signature = Base64( HMAC-SHA1( Your_SK, StringToSign ) )
StringToSign = Base64( UTF-8-Encoding-Of( policy ) )
```
签名的计算过程如下：
1. 构造请求字符串StringToSign，即对policy先进行UTF8编码，然后再进行Base64编码。
2. 使用SK对第一步的结果进行HMAC-SHA1签名计算。
3. 对第二步的结果进行Base64编码，得到签名。
图1计算表单签名   
![](https://support.huaweicloud.com/api-obs/figure/zh-cn_image_0000001955242814.png "点击放大")
#### 签名代码示例
以下是计算表单中携带签名的示例代码：
- [Java中签名的计算方法]
  ```
  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();
      }
      // 构造StringToSign
      private String stringToSign(String[] tmpConditions, String expiration) {
          List 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是时间戳形式，例如：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().decode(demo.DEFAULT_ENCODING)
      signature = demo.post_signature(post_policy).decode(demo.DEFAULT_ENCODING)
      # 表单中携带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)
  ```
