QingCloud 文档 – API 使用示例 - kaiyun官方网站下载手机网页 /storage/object-storage/api/practices/ Recent content in API 使用示例 on QingCloud 文档 Hugo -- gohugo.io Wed, 25 Nov 2020 10:08:56 +0900 Storage: 数据访问权限 /storage/object-storage/api/practices/access_control/ Fri, 28 Feb 2020 10:08:56 +0900 /storage/object-storage/api/practices/access_control/ <p>QingStor 对象存储通过请求中的 <a href="../signature">签名验证</a> 信息来验证用户身份。签名验证中的 Access Key,需要用户事先从 <a href="/storage/object-storage/api/practices/signature/#%E8%8E%B7%E5%8F%96-access-key">控制台申请</a> 并配置到应用程序中。没有经过签名的请求 QingStor 对象存储称之为匿名用户。本章主要介绍经过 QingStor 对象存储验证用户身份之后如何进行权限的控制。</p> <p>QingStor 对象存储的数据访问权限设置方式有三种:</p> <ul> <li>Bucket Policy (存储空间策略)</li> <li>Bucket ACL (存储空间访问控制)</li> <li>CORS (跨源资源共享)</li> </ul> <h2 id="存储空间策略bucket-policy">存储空间策略(Bucket Policy)</h2> <p>通过 Bucket Policy,用户可以向其他开·云app官方版下载 QingCloud 用户赋予 QingStor 对象存储相应 Bucket 及其 Object 的访问权限,其语义覆盖 API ACL,Resource ACL。</p> <p>Bucket Policy 作为一种数据访问权限设置方式,其对 QingStor 对象存储资源的访问权限的控制粒度是介于 Bucket ACL 和 Object ACL (尚未推出) 之间的。Bucket Policy 可以细致地指定 API 级别的控制,从而实现 Bucket ACL 和 Object ACL 所不能实现的一些功能,如:<a href="/storage/object-storage/beat-practices/policy/">防盗链</a>。</p> <p>一般情况下通过 Qingstor 对象存储的 <a href="/storage/object-storage/manual/console/bucket_manage/access_control/#%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4%E7%AD%96%E7%95%A5bucket-policy">Console 界面</a> 进行配置即可,也支持通过 <a href="/storage/object-storage/api/bucket/policy/">API</a> 来设置。</p> <p>用户可以使用 Bucket Policy 中的 Condition 实现一些条件访问权限设置,比如防盗链。Condition 的详细介绍请参见 <a href="../../bucket/policy/put_policy/#condition-%E8%AF%B4%E6%98%8E">Bucket Policy Condition</a></p> <h2 id="存储空间访问控制bucket-acl">存储空间访问控制(Bucket ACL)</h2> <p>Bucket ACL 对于 QingStor 对象存储的资源访问权限的控制粒度比起 Bucket Policy 和 Object ACL 要更大一些,使用起来也更加简单。其所授予被授权者的权限列表仅可为 READ (可读),WRITE (可写),或 FULL_CONTROL (可读写)。</p> <p>Bucket 拥有者默认就拥有该 Bucket 的所有权限。当然,用户可以另外配置 Bucket 的公开读或公开写,即不附带认证信息的匿名访问,也可以针对特定开·云app官方版下载用户来配置该 Bucket 的读写权限。</p> <p>一般情况下通过 Qingstor 对象存储的 <a href="../../../manual/console/bucket_manage/access_control/#%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6bucket-acl">Console 界面</a> 进行配置即可,也支持通过 <a href="../../bucket/acl/">API</a> 来设置。</p> <h2 id="访问权限匹配规则">访问权限匹配规则</h2> <p>针对每个请求,Bucket Policy 的优先级高于 Bucket ACL。当某个请求被 Bucket Policy 允许或拒绝时,Bucket ACL 将被忽略;当该请求未被 Bucket Policy 匹配时,QingStor 对象存储服务端将继续检查 Bucket ACL 以确定该请求是否该被执行。</p> <p>为了安全考虑,QingStor 对象存储做出如下规定:</p> <ul> <li>Bucket 创建,删除,监控信息等 API 不允许匿名访问。</li> <li>删除 Bucket,ACL 以及 Policy 等设置类 API 只允许 Bucket 拥有者自己调用。</li> <li>获取 Object 列表默认情况下不允许匿名调用 (无论是否设置公开可读),需要显式配置 Bucket Policy 指定后才可匿名调用。</li> </ul> <h2 id="跨源资源共享cors">跨源资源共享(CORS)</h2> <p>当用户使用 JavaScript AJAX 向 QingStor 对象存储发起的请求属于跨源请求时,默认情况下浏览器为了安全考虑,不允许调用不同域名下的资源。因为用户的网站域名跟 Qingstor 对象存储的域名不一致,这种情况下需要为 Bucket 配置 CORS 规则.</p> <p>CORS 可以对请求来源的域名和请求头进行控制。</p> <p>一般情况下通过 Qingstor 对象存储的 <a href="../../../manual/console/bucket_manage/access_control/#%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4%E7%9A%84%E8%B7%A8%E6%BA%90%E8%B5%84%E6%BA%90%E5%85%B1%E4%BA%AB%E7%AD%96%E7%95%A5bucket-cors">Console 界面</a> 进行配置即可,也支持通过 <a href="../../bucket/cors/">API</a> 来设置。</p> <h2 id="示例">示例</h2> <h3 id="匹配规则示例">匹配规则示例</h3> <p>假设用户需将 Bucket 共享给另一个用户,这个用户可以是另一个开·云app官方版下载 QingCloud 用户,也可以是当前用户创建的子帐号。现对该 Bucket 设置的 Bucket ACL 和 Bucket Policy 规则如下:</p> <ul> <li>设置 ACL 为:用户拥有可读写权限</li> </ul> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#e6db74">&#34;ACL&#34;</span><span style="color:#960050;background-color:#1e0010">:</span>{<span style="color:#f92672">&#34;user-henry&#34;</span>: <span style="color:#e6db74">&#34;FULL_CONTROL&#34;</span>} </span></span></code></pre></div><ul> <li>设置 Policy 为:拒绝用户执行删除 Object 的操作</li> </ul> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#e6db74">&#34;bucket_policy&#34;</span><span style="color:#960050;background-color:#1e0010">:</span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;statement&#34;</span>: [ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;deny user-henry deleting object from this bucket&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;user&#34;</span>: <span style="color:#e6db74">&#34;user-henry&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;action&#34;</span>: <span style="color:#e6db74">&#34;delete_object&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;effect&#34;</span>: <span style="color:#e6db74">&#34;deny&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;resource&#34;</span>: [<span style="color:#e6db74">&#34;mybucket/*&#34;</span>] </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> ], </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>如上所示,即使用户 <code>user-henry</code> 拥有对 <code>mybucket</code> 的可读写权限,但是因为 Bucket Policy 禁止该用户删除 <code>mybucket</code> 下的所有 Object,所以 <code>user-henry</code> 对 <code>mybucket</code> 发起的 Object 删除操作都将被拒绝。</p> <h3 id="防盗链白名单示例">防盗链白名单示例</h3> <p>此防盗链规则生效的前提为 Bucket ACL 中未设置公开的读权限。</p> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#e6db74">&#34;bucket_policy&#34;</span><span style="color:#960050;background-color:#1e0010">:</span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;statement&#34;</span>: [ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;allow example1.com to get object from this bucket&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;user&#34;</span>: <span style="color:#e6db74">&#34;*&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;action&#34;</span>: <span style="color:#e6db74">&#34;get_object&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;effect&#34;</span>: <span style="color:#e6db74">&#34;allow&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;resource&#34;</span>: [<span style="color:#e6db74">&#34;mybucket/*&#34;</span>], </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;condition&#34;</span>: { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;string_like&#34;</span>: {<span style="color:#f92672">&#34;Referer&#34;</span>: <span style="color:#e6db74">&#34;*.example1.com&#34;</span>}, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;string_not_like&#34;</span>: {<span style="color:#f92672">&#34;Referer&#34;</span>: <span style="color:#e6db74">&#34;*.service.example1.com&#34;</span>} </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> ] </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>其中 <code>*.example1.com</code> 为域名白名单。</p> <h3 id="防盗链黑名单示例">防盗链黑名单示例</h3> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#e6db74">&#34;bucket_policy&#34;</span><span style="color:#960050;background-color:#1e0010">:</span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;statement&#34;</span>: [ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;deny example2.com getting object&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;user&#34;</span>: <span style="color:#e6db74">&#34;*&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;action&#34;</span>: <span style="color:#e6db74">&#34;get_object&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;effect&#34;</span>: <span style="color:#e6db74">&#34;deny&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;resource&#34;</span>: [<span style="color:#e6db74">&#34;mybucket/*&#34;</span>], </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;condition&#34;</span>: { </span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;string_like&#34;</span>: {<span style="color:#f92672">&#34;Referer&#34;</span>: <span style="color:#e6db74">&#34;*.example2.com&#34;</span>} </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> ] </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>其中 <code>*.example2.com</code> 为域名黑名单。</p> Storage: 签名验证 /storage/object-storage/api/practices/signature/ Mon, 01 Jan 0001 00:00:00 +0000 /storage/object-storage/api/practices/signature/ <p>QingStor 对象存储具备多种访问方式。根据场景的不同,QingStor 对象存储提供了相应的方案来进行请求的身份认证。由于实现签名需要考虑的细节较多,包括语言编码、特殊字符处理、以及新 API 的支持等,所以建议开发者直接使用 QingStor 对象存储提供的 SDK 或客户端工具,以减少开发和维护工作。</p> <p>若用户的 Bucket 设为私有权限,需要通过请求进行签名;若用户的 Bucket 已经配置为公开读或公开读写权限,则不需要对请求进行签名。</p> <h2 id="签名方式">签名方式</h2> <h3 id="请求头签名">请求头签名</h3> <p>该方式适用于大部分的场景,如运行在服务器端的应用程序向 QingStor 对象存储服务端发起访问请求。应用程序需配置 Access Key。为了防止签名请求被恶意用户拦截重放,从保证用户数据安全的角度出发,QingStor 对象存储设置了 15 分钟之后签名串失效的机制。</p> <p>所以,用户需要将系统时间通过 NTP 校准到网络时间。若用户的服务器是运行在开·云app官方版下载 IaaS 平台上的虚拟机,系统会自动同步标准时间。</p> <h3 id="请求参数签名">请求参数签名</h3> <p>该方式支持所有类型的 API 请求,适合在客户端无法设置请求头时使用。其中典型的场景包括:由服务端程序调用 SDK 生成带一定过期时间的参数签名的 URL,显示在网页上作为下载链接。</p> <h3 id="表单签名">表单签名</h3> <p>该方式适用于浏览器直接通过 HTML 表单方式上传小文件。详情请参考 <a href="../../object/post/">Post Object API</a></p> <h3 id="签名服务">签名服务</h3> <p>由于移动端应用的特点,将 Access Key 随 APP 分发会带来安全问题。所以 QingStor 对象存储提供了签名服务解决方案,具体请参考 <a href="../../../beat-practices/app_integration/">移动 App 接入方案</a>。</p> <p>移动客户端每次上传下载文件之前,由 QingStor 对象存储服务端验证了用户身份之后,签名服务根据特定的 QingStor API 调用参数,生成头签名或参数签名,返回给客户端。客户端使用该签名直接跟 Qingstor 对象存储进行交互。</p> <p>适用于Ajax类型应用的 <a href="/storage/object-storage/sdk/javascript/">Javascript SDK</a>、适用 Android 平台的 <a href="/storage/object-storage/sdk/java/">Java SDK</a> 均支持构造 API 请求时应用服务端计算的签名串。</p> <p>由于移动客户端经常会遇到系统时间不准确的问题,且签名计算结果跟时间又密切相关,这就需要签名服务在计算签名时把所用的标准时间一并返回给客户端,设置正确的 <code>Date header</code>。</p> <h2 id="获取-access-key">获取 Access Key</h2> <p>QingStor 对象存储通过使用对称加密的方法来验证请求者的身份。如果用户以个人身份请求 QingStor 对象存储服务时,首先需要拥有一对密钥。密钥包括 <code>Access Key ID</code> 和 <code>Secret Access Key</code>,其中 <code>Secret Access Key</code> 在签名的过程中需要用到。</p> <h3 id="注意事项">注意事项</h3> <ul> <li>获得密钥的用户,可以以密钥拥有者的身份访问 QingStor 对象存储,所以密钥中的 Secret Access Key 部分需要对外保密。</li> <li>API 密钥下载链接,自创建 10 分钟后失效。用户需及时下载并保存。</li> </ul> <h3 id="操作步骤">操作步骤</h3> <ol> <li>登录 <a href="https://console.qingcloud.com/login">管理控制台</a>,选择 <strong>产品与服务</strong> &gt; <strong>访问与授权</strong> &gt; <strong>API 密钥</strong>,进入 API 密钥主页面:</li> </ol> <p><img src="/storage/object-storage/_images/ak_sk_1.png" alt=""></p> <ol start="2"> <li>在 API 密钥主页面,点击 <strong>创建</strong>,按要求输入相关信息后,点击 <strong>提交</strong> 按钮:</li> </ol> <p><img src="/storage/object-storage/_images/create_access_key.png" alt=""></p> <ol start="3"> <li>在随后的对话框中下载密钥文件:</li> </ol> <p><img src="/storage/object-storage/_images/down_access_key.png" alt=""></p> <ol start="4"> <li>用文本查看 API 密钥信息:</li> </ol> <p><img src="/storage/object-storage/_images/ak_sk_2.png" alt=""></p> <p><strong>说明</strong></p> <p>如上图所示,Access Key ID 和 Secret Access Key 分别为:<br><code>HOKUMWFMUIDFDIWEKLCA</code> 与 <code>YD0vaP82NQX6R7t0lZVtNtC1JsnkGxgFcXAiwLCN</code>。</p> <h2 id="请求头签名示例">请求头签名示例</h2> <p>这里以请求头签名的方式为例加,对签名串的创建过程,进行说明。</p> <h3 id="构建签名串">构建签名串</h3> <p>签名串 (String To Sign) 的总体构成有如下几个部分:</p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">string_to_sign = Verb + &#34;\n&#34; + Content-MD5 + &#34;\n&#34; + Content-Type + &#34;\n&#34; + Date + &#34;\n&#34; (+ Canonicalized Headers + &#34;\n&#34;) + Canonicalized Resource </code></pre><ul> <li><code>Verb</code>:HTTP Method,包括 HEAD,GET,PUT,DELETE,OPTIONS。</li> <li><code>Content-MD5</code>:请求内容数据的 MD5 值,和请求头里的字段值保持一致,若请求头没有这个参数,保留空白行。</li> <li><code>Content-Type</code>:请求内容的类型,和请求头里的字段值保持一致,若请求头没有这个参数,保留空白行。</li> <li><code>Date</code>:此次请求的时间,需要符合 HTTP 规定的 GMT 格式。</li> <li><code>Canonicalized Headers</code>:请求头中以 <code>x-qs-</code> 开头的字段。若该值为空,不保留空白行。构建方式参考 <a href="#%E6%9E%84%E5%BB%BA-canonicalized-headers">下文内容</a>。</li> <li><code>Canonicalized Resource</code>:请求访问的资源。构建方式参考 <a href="#%E6%9E%84%E5%BB%BA-canonicalized-resource">下文内容</a>。</li> </ul> <p><strong>签名串示例</strong></p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">PUT 4gJE4saaMU4BqNR0kLY+lw== image/jpeg Wed, 10 Dec 2014 17:20:31 GMT /mybucket/%28%27this%20is%20test%27%2C%29 </code></pre><h3 id="构建-canonicalized-headers">构建 Canonicalized Headers</h3> <p>下面介绍签名串中 <code>Canonicalized Headers</code> 和 <code>Canonicalized Resource</code> 的拼接方法:</p> <ol> <li>将所有以 <code>x-qs-</code> (不区分大小写) 为前缀的 HTTP 请求头的名字转换成小写字母。例如 <code>X-QS-Date: Wed, 10 Dec 2014 17:20:31 GMT</code> 转换成 <code>x-qs-date: Wed, 10 Dec 2014 17:20:31 GMT</code>。如果某些请求头的值在发出的时候被 URL 编码,那么计算签名时也应当是 URL 编码之后的值。</li> <li>将上一步得到的所有 HTTP 请求头按照请求头名字部分的字典升序排列。</li> <li>删除请求头和内容之间分隔符两端出现的任何空格。例如 <code>x-qs-date: Wed, 10 Dec 2014 17:20:31 GMT</code> 转换成 <code>x-qs-date:Wed, 10 Dec 2014 17:20:31 GMT</code></li> <li>将所有的头和内容用 <code>\n</code> 分隔符分隔拼成最后的 Canonicalized Headers。</li> </ol> <p><strong>含有 Canonicalized Headers 的签名串示例:</strong></p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">PUT 4gJE4saaMU4BqNR0kLY+lw== image/jpeg x-qs-copy-source:/mybucket/%E4%B8%AD%E6%96%87 x-qs-copy-source-if-match:%22199389a12492266114933fc428e8cfdc%22 x-qs-date:Wed, 10 Dec 2014 17:20:31 GMT /mybucket/%28%27this%20is%20test%27%2C%29 </code></pre><h3 id="构建-canonicalized-resource">构建 Canonicalized Resource</h3> <ol> <li>若请求 URL 为 Virtual-host 风格,则设置初始字符串为 <code>/&lt;bucket-name&gt;</code>。若请求 URL 为 Path 风格,则设置初始字符串为空。</li> <li>在第1步得到的字符串末尾追加 URI Path。与请求头中的请求字段 <code>path</code> 一致,即 URI 编码后的值。</li> <li>若请求包括子资源,例如 ACL 信息等,那么需将所有的子资源按照字典序从小到大排列,以 <code>&amp;</code> 拼接生成子资源字符串,并以 <code>?</code> 开头追加至字符串结尾。</li> <li>除了上述子资源以外,形如 <code>response-*</code> 的参数,如 <a href="/storage/object-storage/api/object/basic_opt/get/">GET Object</a>,也需要按照上述规则拼接至 <code>Canonicalized Resource</code> 中。</li> </ol> <p><strong>全部的子资源包括:</strong></p> <ul> <li>acl</li> <li>append</li> <li>cors</li> <li>cname</li> <li>delete</li> <li>image</li> <li>logging</li> <li>lifecycle</li> <li>mirror</li> <li>notification</li> <li>policy</li> <li>position</li> <li>part_number</li> <li>replication</li> <li>stats</li> <li>uploads</li> <li>upload_id</li> </ul> <p>经过上面步骤得到的 <code>Canonicalized Resource</code>,应该类似于:</p> <p><strong>List Objects:</strong></p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">/mybucket/ </code></pre><p><strong>不含子资源的请求:</strong></p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">/mybucket/photo.jpg </code></pre><p><strong>包含子资源的请求:</strong></p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">/mybucket/movie.mov?uploads </code></pre><p><strong>包含多个子资源的请求:</strong></p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">/mybucket/movie.mov?part_number=3&amp;upload_id=dbb3d762975711e6b457525441715ab4 </code></pre><p>无论 URL 为 Virtual-host 风格或是 Path 风格,最终得到的签名字符串是一致的。</p> <h3 id="计算签名-signature">计算签名 Signature</h3> <p>假设经过前文步骤得到的签名串为 <code>string_to_sign</code>,接下来的步骤是对签名串进行签名。</p> <ol> <li> <p>将 API 密钥的私钥 <code>secret_access_key</code> 作为 Key,使用 Hmac sha256 算法给签名串生成签名:</p> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> hmac </span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> hashlib <span style="color:#f92672">import</span> sha256 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>h <span style="color:#f92672">=</span> hmac<span style="color:#f92672">.</span>new(secret_access_key, digestmod<span style="color:#f92672">=</span>sha256) </span></span><span style="display:flex;"><span>h<span style="color:#f92672">.</span>update(string_to_sign) </span></span></code></pre></div></li> <li> <p>将签名进行 Base64 编码:</p> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> base64 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>signature <span style="color:#f92672">=</span> base64<span style="color:#f92672">.</span>b64encode(h<span style="color:#f92672">.</span>digest())<span style="color:#f92672">.</span>strip() </span></span></code></pre></div></li> </ol> <h3 id="添加签名-authorization">添加签名 Authorization</h3> <p>添加 HTTP 请求头:</p> <pre tabindex="0"><code>Authorization: QS &lt;access_key_id&gt;:&lt;signature&gt; </code></pre><p>请求头示例:</p> <pre tabindex="0"><code>Authorization: QS PLLZOBTTZXGBNOWUFHZZ:tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO+T2rxomBQ= </code></pre><h2 id="针对-javascript-客户端的特殊考虑">针对 JavaScript 客户端的特殊考虑</h2> <p>由于在浏览器环境中,<code>Date</code> 字段是被保护的不能设置,所以 JavaScript SDK 在签名时需要将 <code>Date</code> 头字段留空,并且设置 <code>x-qs-date</code> 头字段。以下是一个 JavaScript 客户端的示例:</p> <p><strong>请求</strong></p> <pre tabindex="0"><code>GET / HOST: js-sdk-test.pek3a.qingstor.com x-qs-date: Fri, 04 May 2018 16:37:00 GMT </code></pre><p><strong>签名串 (string_to_sign)</strong></p> <pre tabindex="0"><code>GET application/octet-stream x-qs-date:Fri, 04 May 2018 16:37:00 GMT /js-sdk-test </code></pre><h2 id="请求参数签名示例">请求参数签名示例</h2> <p>在一些使用场景中可能不便于设置请求头,比如使用浏览器重定向请求,或者给其它用户分享下载链接。QingStor 对象存储允许使用请求参数签名的方式,替代请求头签名。</p> <p>需要注意的是,该方式需要设定请求过期时间 <code>expires</code> 参数。在过期时间之后到达的请求将被 QingStor 对象存储拒绝。</p> <p>请求参数签名的方式不需要在 HTTP 请求头中附加任何内容,只需在请求参数中添加以下三项 <strong>必要参数</strong>:</p> <table> <thead> <tr> <th>参数</th> <th>描述</th> <th>示例</th> </tr> </thead> <tbody> <tr> <td>access_key_id</td> <td>在开·云app官方版下载 QingCloud 控制台申请的 Access Key ID</td> <td>PLLZOBTTZXGBNOWUFHZZ</td> </tr> <tr> <td>expires</td> <td>签名过期时间,该时间为 Unix Time (也称为 Epoch Time),表示方法是自历元 <code>1970-01-01 00:00,The Epoch</code> 之后的秒数,类型为整数。</td> <td>1479107162</td> </tr> <tr> <td>signature</td> <td>签名串 <code>string_to_sign</code> 经过 HMAC-SHA256 加密后,再使用 Base64 编码,最后使用 URI 编码后的结果</td> <td>tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D</td> </tr> </tbody> </table> <p><strong>构建签名串:</strong></p> <p>相比于请求头签名方法,签名串 <code>string_to_sign</code> 的生成过程只有一点不同,需把 <code>Date</code> 替换为 <code>Expires</code>:</p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">string_to_sign = Verb + &#34;\n&#34; + Content-MD5 + &#34;\n&#34; + Content-Type + &#34;\n&#34; + Expires + &#34;\n&#34; (+ Canonicalized Headers + &#34;\n&#34;) + Canonicalized Resource </code></pre><p><strong>计算签名:</strong></p> <p>通过上述方法得到签名串 <code>string_to_sign</code> 以后计算 <code>signature</code>。需注意的是,<code>signature</code> 必须进行 URI 编码,比如加号 <code>+</code> 要被编码成 <code>%2B</code>。</p> <div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> hmac </span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> base64 </span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> urllib </span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> hashlib <span style="color:#f92672">import</span> sha256 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>h <span style="color:#f92672">=</span> hmac<span style="color:#f92672">.</span>new(secret_access_key, digestmod<span style="color:#f92672">=</span>sha256) </span></span><span style="display:flex;"><span>h<span style="color:#f92672">.</span>update(string_to_sign) </span></span><span style="display:flex;"><span>signature <span style="color:#f92672">=</span> urllib<span style="color:#f92672">.</span>quote(base64<span style="color:#f92672">.</span>b64encode(h<span style="color:#f92672">.</span>digest())<span style="color:#f92672">.</span>strip()) </span></span></code></pre></div><p><strong>拼接请求参数:</strong></p> <p>将以上计算得出的字符串,拼接成请求参数,追加到请求地址的后面:</p> <pre tabindex="0"><code class="language-plain_text" data-lang="plain_text">access_key_id=PLLZOBTTZXGBNOWUFHZZ&amp;expires=1479107162&amp;signature=tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D </code></pre><p><strong>请求示例:</strong></p> <p>最终发出去的请求示例:</p> <pre tabindex="0"><code>GET /music.mp3?access_key_id=PLLZOBTTZXGBNOWUFHZZ&amp;expires=1479107162&amp;signature=tuXu/KcggHWPAfEmraUHDwEUdiIPSXVRsO%2BT2rxomBQ%3D Host: mybucket.pek3a.qingstor.com Date: Mon, 14 Nov 2016 14:05:00 GMT </code></pre>