

# 对 REST 请求进行签名和身份验证（Amazon 签名版本 2）
<a name="RESTAuthentication"></a>

**Topics**
+ [使用临时安全凭证](#UsingTemporarySecurityCredentials)
+ [身份验证标头](#ConstructingTheAuthenticationHeader)
+ [用于签名的请求规范](#RESTAuthenticationRequestCanonicalization)
+ [构建 CanonicalizedResource 元素](#ConstructingTheCanonicalizedResourceElement)
+ [构建 CanonicalizedAmzHeaders 元素](#RESTAuthenticationConstructingCanonicalizedAmzHeaders)
+ [位置和命名 HTTP 标头 StringToSign 元素的对比](#RESTAuthenticationStringToSign)
+ [时间戳要求](#RESTAuthenticationTimeStamp)
+ [身份验证示例](#RESTAuthenticationExamples)
+ [REST 请求签名问题](#RESTAuthenticationDebugging)
+ [查询字符串请求身份验证替代项](#RESTAuthenticationQueryStringAuth)

**注意**  
本主题说明使用签名版本 2 对请求进行身份验证。Amazon S3 现在支持最新的 Signature Version 4。所有区域都支持此最新签名版本，而 2014 年 1 月 30 日之后的所有新区域都只支持签名版本 4。有关更多信息，请转至《*Amazon Simple Storage Service API 参考*》中的[验证请求 (Amazon Signature Version 4)](https://docs.amazonaws.cn/AmazonS3/latest/API/sig-v4-authenticating-requests.html)。

 身份验证是指向系统证明身份的过程。身份对于 Amazon S3 访问控制决策非常重要。将根据请求者的身份允许请求或拒绝部分请求。例如，将为已注册的开发人员保留创建存储桶的权利，并且 (默认) 将为相关的存储桶拥有者保留在存储桶中创建对象的权利。作为开发人员，您需要提出请求来调用这些权限，因此您需要对您的请求进行身份验证以向系统证明您的身份。本节向您演示了应如何进行操作。

**注意**  
 本节中的内容不适用于 HTTP POST。有关更多信息，请参阅 [使用 POST (Amazon Signature Version 2) 的基于浏览器的上传](UsingHTTPPOST.md)。

 Amazon S3 REST API 使用基于密钥 HMAC (Hash Message Authentication Code) 的自定义 HTTP 方案进行身份验证。要对请求进行身份验证，您首先需要合并请求的选定元素以形成一个字符串。然后，您可以使用 Amazon 秘密访问密钥来计算该字符串的 HMAC。通常我们将此过程称为“签署请求”并且我们将输出 HMAC 算法称为“签名”，因为它会模拟真实签名的安全属性。最后，您可以使用本部分中介绍的语法，作为请求的参数添加此签名。

 系统收到经身份验证的请求时，将提取您申领的 Amazon 秘密访问密钥，并以相同的使用方式将它用于计算已收到的消息的签名。然后，它会将计算出的签名与请求者提供的签名进行对比。如果两个签名相匹配，则系统认为请求者必须拥有对 Amazon 秘密访问密钥的访问权限，因此充当向其颁发密钥的委托人的颁发机构。如果两个签名不匹配，那么请求将被丢弃，同时系统将返回错误消息。

**Example 经身份验证的 Amazon S3 REST 请求**  

```
1. GET /photos/puppy.jpg HTTP/1.1
2. Host: awsexamplebucket1.us-west-1.s3.amazonaws.com
3. Date: Tue, 27 Mar 2007 19:36:42 +0000
4. 
5. {{Authorization: AWS AKIAIOSFODNN7EXAMPLE:
6. qgk2+6Sv9/oM7G3qLEjTH1a1l1g=}}
```

## 使用临时安全凭证
<a name="UsingTemporarySecurityCredentials"></a>

如果您使用临时安全凭证签署请求 (参阅 [提出请求](MakingRequests.md))，您必须通过添加 `x-amz-security-token` 标头将相应的安全令牌包含在您的请求中。

使用 Amazon Security Token Service API 获取临时安全凭证时，响应包含临时安全凭证和会话令牌。向 Amazon S3 发送请求时，您可以在 `x-amz-security-token` 标头中提供会话令牌值。有关 IAM 提供的 Amazon Security Token Service API 的信息，请转到《*Amazon Security Token Service API 参考指南*》中的[操作](https://docs.amazonaws.cn/STS/latest/APIReference/API_Operations.html)。

## 身份验证标头
<a name="ConstructingTheAuthenticationHeader"></a>

Amazon S3 REST API 使用标准的 HTTP `Authorization` 标头来传递身份验证信息。（标准标头的名称是不可取的，因为它承载的是身份验证信息，而不是授权。） 在 Amazon S3 身份验证方案下，授权标头具有以下形式：

```
1. Authorization: AWS {{AWSAccessKeyId}}:{{Signature}}
```

注册后，会向开发人员发放 Amazon 访问密钥 ID 和 Amazon 秘密访问密钥。对于请求身份验证，`AWSAccessKeyId` 元素将标识用于计算签名的访问密钥 ID 和进行请求的开发人员 (间接)。

`Signature` 元素是请求中选定元素的 RFC 2104HMAC-SHA1，因此授权标头的 `Signature` 部分会因请求的不同而各异。如果系统计算出的请求签名与请求随附的 `Signature` 相匹配，则请求者拥有经证明的 Amazon 秘密访问密钥。然后，在该身份下，借助获得此密钥的开发人员的授权来处理请求。

以下是说明 `Authorization` 请求标头结构的伪语法。(在该示例中，`\n` 表示 Unicode 码位 `U+000A`，这通常称为换行符)。

```
 1. Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
 2. 
 3. Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of(YourSecretAccessKey), UTF-8-Encoding-Of( StringToSign ) ) );
 4. 
 5. StringToSign = HTTP-Verb + "\n" +
 6. 	Content-MD5 + "\n" +
 7. 	Content-Type + "\n" +
 8. 	Date + "\n" +
 9. 	CanonicalizedAmzHeaders +
10. 	CanonicalizedResource;
11. 
12. CanonicalizedResource = [ "/" + Bucket ] +
13. 	<HTTP-Request-URI, from the protocol name up to the query string> +
14. 	[ subresource, if present. For example "?acl", "?location", or "?logging"];
15. 
16. CanonicalizedAmzHeaders = <described below>
```

 HMAC-SHA1 是由 [RFC 2104（用于消息身份验证的哈希密钥）](http://www.ietf.org/rfc/rfc2104.txt)定义的算法。该算法要求输入两个字节字符串：一个密钥和一个消息。对于 Amazon S3 请求身份验证，请将您的 Amazon 秘密访问密钥 (`YourSecretAccessKey`) 用作密钥，并将 UTF-8 编码格式的 `StringToSign` 用作消息。HMAC-SHA1 的输出也是字节字符串，称为摘要。通过对此摘要进行 Base64 编码来构成 `Signature` 请求参数。

## 用于签名的请求规范
<a name="RESTAuthenticationRequestCanonicalization"></a>

 在系统收到经身份验证的请求时撤销，系统会将计算出的请求签名与 `StringToSign` 中提供的签名进行对比。基于此原因，您必须采用 Amazon S3 使用的相同方法计算签名。根据适用于签名的统一格式放置请求的过程称为*标准化*。

## 构建 CanonicalizedResource 元素
<a name="ConstructingTheCanonicalizedResourceElement"></a>

 `CanonicalizedResource` 表示请求的目标 Amazon S3 资源。为 REST 请求构建它，如下所示：

1.  使用空字符串 (`""`) 启动。

1. 如果请求使用 HTTP 主机标头 (虚拟托管类型) 来指定请求，请在 `"/"` (例如，“/bucketname”) 的后面附加存储桶名称。对于路径类型请求和不会寻址存储桶的请求，不执行任何操作。有关虚拟托管类型请求的更多信息，请参阅[存储桶的虚拟托管](https://docs.amazonaws.cn//AmazonS3/latest/userguide/VirtualHosting.html)。

   对于虚拟托管类型请求“https://awsexamplebucket1.s3.us-west-1.amazonaws.com/photos/puppy.jpg”，`CanonicalizedResource` 是“/awsexamplebucket1”。

   对于路径类型请求“https://s3.us-west-1.amazonaws.com/awsexamplebucket1/photos/puppy.jpg”，`CanonicalizedResource` 为 ""。

1. 附加未解码的 HTTP 请求-URI 的路径部分 (取决于但不包括查询字符串)。

   对于虚拟托管类型请求“https://awsexamplebucket1.s3.us-west-1.amazonaws.com/photos/puppy.jpg”，`CanonicalizedResource` 是“/awsexamplebucket1/photos/puppy.jpg”。

   对于路径类型的请求，“https://s3.us-west-1.amazonaws.com/awsexamplebucket1/photos/puppy.jpg”，`CanonicalizedResource` 是“/awsexamplebucket1/photos/puppy.jpg”。此时，虚拟托管类型和路径类型请求的 `CanonicalizedResource` 相同。

   对于不寻址存储桶的请求（例如，[GET Service](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTServiceGET.html)），请附加“/”。

1. 如果请求将寻址子资源（例如，`?versioning`、`?location`、`?acl`、`?lifecycle` 或 `?versionid`），请附加子资源、其值（如果有）和问号。请注意，如果存在多个子资源，子资源必须按子资源名称的字典顺序排序并使用“&”进行分隔 (例如，?acl&versionId={{值}})。

   构建 CanonicalizedResource 元素时必须包含的子资源为：acl、lifecycle、location、logging、notification、partNumber、policy、requestPayment、uploadId、uploads、versionId、versioning、versions 和 website。

   如果请求指定覆盖响应标头值的查询字符串参数（请参阅 [Get Object](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectGET.html)），请附加查询字符串参数及其值。签名时，请勿对这些值进行编码；但是进行请求时，您必须对这些参数值进行编码。GET 请求中的查询字符串参数包括 `response-content-type`、`response-content-language`、`response-expires`、`response-cache-control`、`response-content-disposition` 和 `response-content-encoding`。

   为多对象删除请求创建 CanonicalizedResource 时，必须包括 `delete` 查询字符串参数。

必须按照元素在 HTTP 请求中的显示方式，使用字母 (包括 URL 编码元字符) 签署来自 HTTP 请求-URI 的 CanonicalizedResource 元素。

`CanonicalizedResource` 可能与 HTTP 请求-URI 不同。尤其是，如果您的请求使用 HTTP `Host` 标头来指定存储桶，存储桶不会在 HTTP 请求 URI 中显示。但是，`CanonicalizedResource` 仍然会包含该存储桶。查询字符串参数可能也会在请求-URI 中显示，但是不会包括在 `CanonicalizedResource` 中。有关更多信息，请参阅[存储桶的虚拟托管](https://docs.amazonaws.cn//AmazonS3/latest/userguide/VirtualHosting.html)。

## 构建 CanonicalizedAmzHeaders 元素
<a name="RESTAuthenticationConstructingCanonicalizedAmzHeaders"></a>

要构建 `StringToSign` 的 CanonicalizedAmzHeaders 部分，请选择所有以“x-amz-”开头的 HTTP 请求标头 (使用不区分大小写的对比方式) 并遵循以下步骤。

1.  将每个 HTTP 标头名称转换为小写。例如，“`X-Amz-Date`”改为“`x-amz-date`”。

1.  根据标头名称按字典顺序排列标头集。

1.  将相同名称的标头字段合并为一个“header-name:comma-separated-value-list”对，并按照 RFC 2616 中第 4.2 节中的规定，两个值之间不留空格。例如，可以将元数据标头“`x-amz-meta-username: fred`”和“`x-amz-meta-username: barney`”合并为单个标头“`x-amz-meta-username: fred,barney`”。

1.  通过将折叠空格（包括新建行）替换为单个空格，“展开”跨多个行的长标头（按照 RFC 2616 中第 4.2 节允许的方式）。

1.  删除标头中冒号周围的空格。例如，标头“`x-amz-meta-username: fred,barney`”将变为“`x-amz-meta-username:fred,barney`”。

1.  最后，请向生成的列表中的每个标准化标头附加换行字符 (`U+000A`)。通过将此列表中所有的标头规范化为单个字符串，构建 CanonicalizedResource 元素。

## 位置和命名 HTTP 标头 StringToSign 元素的对比
<a name="RESTAuthenticationStringToSign"></a>

 `StringToSign` 的头几个标头元素（Content-Type、Date 和 Content-MD5）属于位置标头。`StringToSign` 不包括这些标头的名称，仅包括它们在请求中的值。相反，“`x-amz-`”元素会进行命名。标头名称和标头值都会在 `StringToSign` 中显示。

 如果在 `StringToSign` 定义中调用的位置标头不在请求中 (例如，`Content-Type` 或 `Content-MD5` 对于 PUT 请求是可选的，并且对于 GET 请求没有任何意义)，请使用空字符串 ("") 替换该位置。

## 时间戳要求
<a name="RESTAuthenticationTimeStamp"></a>

有效的时间戳对于经身份验证的请求是必须的 (使用 HTTP `Date` 标头或 `x-amz-date` 替代项)。此外，经身份验证的请求随附的客户端时间戳必须处于收到请求时的 Amazon S3 系统时间的 15 分钟之内。否则，请求将失败并出现 `RequestTimeTooSkewed` 错误代码。施加这些限制的目的是为了防止对方重新使用已拦截的请求。要更好地防范窃听，请对经身份验证的请求使用 HTTPS 传输。

**注意**  
对请求日期的验证限制仅适用于不使用查询字符串身份验证的经身份验证的请求。有关更多信息，请参阅 [查询字符串请求身份验证替代项](#RESTAuthenticationQueryStringAuth)。

某些 HTTP 客户端库不提供为请求设置 `Date` 标头的功能。如果您在标准化标头中包含“Date”标头的值时遇到困难，您可以改用“`x-amz-date`”标头为请求设置时间戳。`x-amz-date` 标头的值必须采用其中一种 RFC 2616 格式 ([http://www.ietf.org/rfc/rfc2616.txt](http://www.ietf.org/rfc/rfc2616.txt))。`x-amz-date` 标头位于请求中时，系统将在计算请求签名时忽略任何 `Date` 标头。因此，如果包含了 `x-amz-date` 标头，请在构建 `Date` 时使用 `StringToSign` 的空字符串。有关示例，请参阅下一节。

## 身份验证示例
<a name="RESTAuthenticationExamples"></a>

 本节中的示例使用下表中的 (非有效) 凭证。


| 参数 | 值 | 
| --- | --- | 
| AWSAccessKeyId | AKIAIOSFODNN7EXAMPLE | 
| AWSSecretAccessKey | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY | 

在示例 `StringToSign` 中，格式并不重要；`\n` 是指 Unicode 码位 `U+000A`，通常称为换行符。此外，该示例使用“\+0000”指定时区。您可以改用“GMT”来指定时区，但是在示例中显示的签名将有所不同。

### 对象 GET
<a name="RESTAuthenticationExamples-1"></a>

此示例将从 awsexamplebucket1 存储桶获取一个对象。


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>GET /photos/puppy.jpg HTTP/1.1<br />Host: awsexamplebucket1.us-west-1.s3.amazonaws.com<br />Date: Tue, 27 Mar 2007 19:36:42 +0000<br /><br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:<br />qgk2+6Sv9/oM7G3qLEjTH1a1l1g=}}</pre>  |  <pre>GET\n<br />\n<br />\n<br />Tue, 27 Mar 2007 19:36:42 +0000\n<br />/awsexamplebucket1/photos/puppy.jpg</pre>  | 

 请注意，CanonicalizedResource 包括存储桶名称，但 HTTP 请求 URI 不包括。（存储桶由主机标头指定）。

**注意**  
以下 Python 脚本使用提供的参数来计算前面的签名。您可以使用此脚本来构建您自己的签名，并根据需要替换密钥和 StringToSign。  

```
 1. import base64
 2. import hmac
 3. from hashlib import sha1
 4. 
 5. access_key = '{{AKIAIOSFODNN7EXAMPLE}}'.encode("UTF-8")
 6. secret_key = '{{wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY}}'.encode("UTF-8")
 7. 
 8. string_to_sign = '{{GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/awsexamplebucket1/photos/puppy.jpg}}'.encode("UTF-8")
 9. signature = base64.b64encode(
10.                                 hmac.new(
11.                                          secret_key, string_to_sign, sha1
12.                                          ).digest()
13.                                 ).strip()
14. 
15. 
16. print(f"AWS {access_key.decode()}:{signature.decode()}")
```

### 对象 PUT
<a name="RESTAuthenticationExamples-2"></a>

此示例将向 awsexamplebucket1 存储桶放入一个对象。


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>PUT /photos/puppy.jpg HTTP/1.1<br />Content-Type: image/jpeg<br />Content-Length: 94328<br />Host: awsexamplebucket1.s3.us-west-1.amazonaws.com<br />Date: Tue, 27 Mar 2007 21:15:45 +0000<br /><br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:<br />iqRzw+ileNPu1fhspnRs8nOjjIA=<br />}}</pre>  |  <pre>PUT\n<br />\n<br />image/jpeg\n<br />Tue, 27 Mar 2007 21:15:45 +0000\n<br />/awsexamplebucket1/photos/puppy.jpg</pre>  | 

 请注意请求和 StringToSign 中的 Content-Type 标头。另请注意，由于 Content-MD5 不在请求中，它在 StringToSign 中将保留为空白。

### 列表
<a name="RESTAuthenticationExamples-3"></a>



此示例将列出 awsexamplebucket1 存储桶的内容。


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>GET /?prefix=photos&max-keys=50&marker=puppy HTTP/1.1<br />User-Agent: Mozilla/5.0<br />Host: awsexamplebucket1.s3.us-west-1.amazonaws.com<br />Date: Tue, 27 Mar 2007 19:42:41 +0000<br /><br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:<br />m0WP8eCtspQl5Ahe6L1SozdX9YA=}}</pre>  |  <pre>GET\n<br />\n<br />\n<br />Tue, 27 Mar 2007 19:42:41 +0000\n<br />/awsexamplebucket1/</pre>  | 

 请注意 CanonicalizedResource 上的尾部斜杠以及查询字符串参数的缺失。

### 取回
<a name="RESTAuthenticationExamples-4"></a>

本示例将为“awsexamplebucket1”存储桶提取访问控制策略子资源。


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>GET /?acl HTTP/1.1<br />Host: awsexamplebucket1.s3.us-west-1.amazonaws.com<br />Date: Tue, 27 Mar 2007 19:44:46 +0000<br /><br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:<br />82ZHiFIjc+WbcwFKGUVEQspPn+0=}}</pre>  |  <pre>GET\n<br />\n<br />\n<br />Tue, 27 Mar 2007 19:44:46 +0000\n<br />/awsexamplebucket1/?acl</pre>  | 

 请注意子资源查询字符串参数包含在 CanonicalizedResource 中的方式。

### 删除
<a name="RESTAuthenticationExamples-5"></a>

本示例使用路径类型和日期替代项从“awsexamplebucket1”存储桶删除对象。


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>DELETE /awsexamplebucket1/photos/puppy.jpg HTTP/1.1<br />User-Agent: dotnet<br />Host: s3.us-west-1.amazonaws.com<br />Date: Tue, 27 Mar 2007 21:20:27 +0000<br /><br />x-amz-date: Tue, 27 Mar 2007 21:20:26 +0000<br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:XbyTlbQdu9Xw5o8P4iMwPktxQd8=}}</pre>  |  <pre>DELETE\n<br />\n<br />\n<br />Tue, 27 Mar 2007 21:20:26 +0000\n<br />/awsexamplebucket1/photos/puppy.jpg</pre>  | 

 请注意我们如何使用“x-amz-date”替代方法来指定日期 (假设是因为我们的客户端库阻止我们设置日期)。在这种情况下，`x-amz-date` 优先于 `Date` 标头。因此，签名中的日期条目必须包含 `x-amz-date` 标头的值。

### 上传
<a name="RESTAuthenticationExamples-6"></a>

本示例将对象上传到使用元数据的别名记录虚拟托管类型存储桶。


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>PUT /db-backup.dat.gz HTTP/1.1<br />User-Agent: curl/7.15.5<br />Host: static.example.com:8080<br />Date: Tue, 27 Mar 2007 21:06:08 +0000<br /><br />x-amz-acl: public-read<br />content-type: application/x-download<br />Content-MD5: 4gJE4saaMU4BqNR0kLY+lw==<br />X-Amz-Meta-ReviewedBy: joe@example.com<br />X-Amz-Meta-ReviewedBy: jane@example.com<br />X-Amz-Meta-FileChecksum: 0x02661779<br />X-Amz-Meta-ChecksumAlgorithm: crc32<br />Content-Disposition: attachment; filename=database.dat<br />Content-Encoding: gzip<br />Content-Length: 5913339<br /><br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:<br />jtBQa0Aq+DkULFI8qrpwIjGEx0E=}}</pre>  |  <pre>PUT\n<br />4gJE4saaMU4BqNR0kLY+lw==\n<br />application/x-download\n<br />Tue, 27 Mar 2007 21:06:08 +0000\n<br /><br />x-amz-acl:public-read\n<br />x-amz-meta-checksumalgorithm:crc32\n<br />x-amz-meta-filechecksum:0x02661779\n<br />x-amz-meta-reviewedby:<br />joe@example.com,jane@example.com\n<br />/static.example.com/db-backup.dat.gz</pre>  | 

 请注意对“x-amz-”标头排序、删减多余空格和转换为小写的方式。另请注意，具有相同名称的多个标头使用逗号连接，以分隔值。

 请注意，如何仅使 `Content-Type` 和 `Content-MD5` HTTP 实体标头显示在 `StringToSign` 中。其他 `Content-*` 实体标头不显示。

 同样请注意，`CanonicalizedResource` 包括存储桶名称，但 HTTP 请求 URI 不包括。（存储桶由主机标头指定）。

### 列出我的所有存储桶
<a name="RESTAuthenticationExamples-7"></a>


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>GET / HTTP/1.1<br />Host: s3.us-west-1.amazonaws.com<br />Date: Wed, 28 Mar 2007 01:29:59 +0000<br /><br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:qGdzdERIC03wnaRNKh6OqZehG9s=}}</pre>  |  <pre>GET\n<br />\n<br />\n<br />Wed, 28 Mar 2007 01:29:59 +0000\n<br />/</pre>  | 

### Unicode 密钥
<a name="RESTAuthenticationExamples-8"></a>


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>GET /dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re HTTP/1.1<br />Host: s3.us-west-1.amazonaws.com<br />Date: Wed, 28 Mar 2007 01:49:49 +0000<br />{{Authorization: AWS AKIAIOSFODNN7EXAMPLE:DNEZGsoieTZ92F3bUfSPQcbGmlM=}}</pre>  |  <pre>GET\n<br />\n<br />\n<br />Wed, 28 Mar 2007 01:49:49 +0000\n<br />/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re</pre>  | 

**注意**  
将逐字读取来自请求-URI 的 `StringToSign` 中的元素，包括 URL 编码和大写。

## REST 请求签名问题
<a name="RESTAuthenticationDebugging"></a>

 REST 请求身份验证失败后，系统将使用 XML 错误文档对请求做出响应。此错误文档中包含的信息将帮助开发人员诊断问题。特别是，`StringToSign` 错误文档的 `SignatureDoesNotMatch` 元素将明确告诉您系统使用的请求规范。

某些工具包可能会在您事先不知情的情况下自动插入标头，例如在 PUT 操作期间添加标头 `Content-Type`。在大部分情况下，插入标头的值保持不变，从而使您可以使用诸如 Ethereal 或 tcpmon 等工具来发现丢失的标头。

## 查询字符串请求身份验证替代项
<a name="RESTAuthenticationQueryStringAuth"></a>

您可以通过传递请求信息作为查询字符串参数，而不是使用 `Authorization` HTTP 标头来验证特定类型的请求。这在允许第三方浏览器直接访问您的私有 Amazon S3 数据，而无需代理请求时非常有用。其概念是构建一个“预签名”的请求并将其编码为最终用户浏览器可检索的 URL。此外，您还可以通过指定过期时间来限制预签名请求。

有关使用查询参数对请求进行身份验证的更多信息，请参阅《*Amazon Simple Storage Service API 参考*》中的[对请求进行身份验证：使用查询参数 (Amazon Signature Version 4)](https://docs.amazonaws.cn/AmazonS3/latest/API/sigv4-query-string-auth.html)。有关使用 Amazon SDK 生成预签名 URL 的示例，请参阅[使用预签名 URL 共享对象](https://docs.amazonaws.cn//AmazonS3/latest/userguide/ShareObjectPreSignedURL.html)。

### 创建签名
<a name="CreatingASignature"></a>

以下是查询字符串验证的 Amazon S3 REST 请求示例。

```
1. GET /photos/puppy.jpg
2. ?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Expires=1141889120&Signature=vjbyPxybdZaNmGa%2ByT272YEAiv4%3D HTTP/1.1
3. Host: awsexamplebucket1.s3.us-west-1.amazonaws.com
4. Date: Mon, 26 Mar 2007 19:37:58 +0000
```

查询字符串请求身份验证方法不要求任何特殊的 HTTP 标头，而是将要求的身份验证元素指定为查询字符串参数：


| 查询字符串参数名称 | 示例值 | 说明 | 
| --- | --- | --- | 
| AWSAccessKeyId | AKIAIOSFODNN7EXAMPLE | 您的 Amazon 访问密钥 ID。指定用于签署请求的 Amazon 秘密访问密钥以及（间接）进行请求的开发人员的身份。 | 
| Expires | 1141889120 | 将签名过期时间指定为自 Epoch (1970 年 1 月 1 日 00:00:00 UTC) 以来的秒数。将拒绝在此时间 (根据服务器) 之后收到的请求。 | 
| Signature | vjbyPxybdZaNmGa%2ByT272YEAiv4%3D | 采用 StringToSign 的 HMAC-SHA1 Base64 编码的 URL 编码。 | 

查询字符串请求身份验证方法与普通的方法稍有差异，不同之处仅在于 `Signature` 请求参数和 `StringToSign` 元素的格式。下面的伪语法演示了查询字符串请求身份验证方法。

```
1. Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKey, UTF-8-Encoding-Of( StringToSign ) ) ) );
2. 
3. StringToSign = HTTP-VERB + "\n" +
4.     Content-MD5 + "\n" +
5.     Content-Type + "\n" +
6.     Expires + "\n" +
7.     CanonicalizedAmzHeaders +
8.     CanonicalizedResource;
```

`YourSecretAccessKey` 是在您注册成为 Amazon Web Services 开发人员时，Amazon 分配给您的 Amazon 秘密访问密钥 ID。请注意如何对 `Signature` 进行 URL 编码，以使其适合放置在查询字符串中。另请注意，在 `StringToSign` 中，HTTP `Date` 位置元素已替换为 `Expires`。`CanonicalizedAmzHeaders` 和 `CanonicalizedResource` 是相同的。

**注意**  
在查询字符串身份验证方法中，在计算要签署的字符串时，请勿使用 `Date` 或 `x-amz-date request` 标头。

#### 查询字符串请求身份验证
<a name="query-str-auth-ex"></a>


| 请求 | StringToSign | 
| --- | --- | 
|  <pre>GET /photos/puppy.jpg?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&<br />    Signature=NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D&<br />    Expires=1175139620 HTTP/1.1<br /><br />Host: awsexamplebucket1.s3.us-west-1.amazonaws.com</pre>  |  <pre>GET\n<br />\n<br />\n<br />1175139620\n<br /><br />/awsexamplebucket1/photos/puppy.jpg</pre>  | 

我们假设，在浏览器创建 GET 请求时，它没有提供 Content-MD5 或 Content-Type 标头，也没有设置任何 x-amz- 标头，因此 `StringToSign` 的这些部分都保留为空白。

#### 使用 Base64 编码
<a name="S3_Authentication_Base64"></a>

必须使用 Base64 编码 HMAC 请求签名。Base64 编码将签名转换为可附加到请求的简单 ASCII 字符串。如果要在 URI 中使用诸如加号 (\+)、正斜杠 (/) 和等号 (=) 等可在签名中显示的字符，必须对它们进行编码。例如，如果身份验证代码包括一个加号 (\+) 标志，请在请求中将其编码为 %2B。将正斜杠编码为 %2F，并将等号编码为 %3D。

有关 Base64 编码的示例，请参考 Amazon S3 [身份验证示例](#RESTAuthenticationExamples)。