你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

通过共享密钥进行授权

对存储服务发出的每个请求都必须获得授权,除非请求针对已公开或已签名访问的 Blob 或容器资源。 授权请求的一个选项是使用本文中所述的共享密钥。

重要

为了获得最佳安全性,Microsoft 建议将 Microsoft Entra ID 与托管标识结合使用,尽可能对 blob、队列和表数据的请求进行授权。 与共享密钥授权相较,使用Microsoft Entra ID和托管标识进行授权可提供更高的安全性和易用性。 若要了解详细信息,请参阅使用 Microsoft Entra ID 授权。 若要详细了解托管标识,请参阅 什么是 Azure 资源的托管标识

对于托管在 Azure 外部的资源(例如本地应用程序),可以通过 Azure Arc 使用托管标识。例如,在已启用 Azure Arc 的服务器上运行的应用可以使用托管标识连接到 Azure 服务。 若要了解详细信息,请参阅 使用已启用 Azure Arc 的服务器对 Azure 资源进行身份验证

对于使用共享访问签名 (SAS) 的方案,Microsoft 建议使用用户委托 SAS。 用户委托 SAS 使用Microsoft Entra凭据而不是帐户密钥进行保护。 若要了解共享访问签名,请参阅Create用户委托 SAS

Blob、队列、表和文件服务支持以下适用于版本 2009-09-19 及更高版本的共享密钥授权方案, (适用于 Blob、队列和表服务) 和版本 2014-02-14 及更高版本 (的文件服务) :

  • 用于 Blob、队列和文件服务的共享密钥。 使用共享密钥授权方案对 Blob、队列和文件服务发出请求。 版本 2009-09-19 及更高版本中的共享密钥授权支持增强签名字符串以提高安全性,并要求更新服务以使用此增强签名授权。

  • 用于表服务的共享密钥。 使用共享密钥授权方案通过 REST API 对表服务发出请求。 2009-09-19 及更高版本中表服务的共享密钥授权使用与表服务早期版本中相同的签名字符串。

  • 共享密钥 Lite。 使用共享密钥精简版授权方案对 Blob、队列、表和文件服务发出请求。

    对于 2009-09-19 版及更高版本的 Blob 和队列服务,共享密钥精简版授权支持使用与以前版本的 Blob 和队列服务中对共享密钥支持的相同签名字符串。 因此,可以使用共享密钥 Lite 请求 Blob 和队列服务,而无需更新签名字符串。

授权请求需要两个标头: Datex-ms-date 标头和 Authorization 标头。 以下几节介绍如何构造这些标头。

重要

Azure 存储同时支持 HTTP 和 HTTPS,但强烈建议使用 HTTPS。

注意

可以通过设置容器的权限,使容器或 Blob 可供公共访问。 有关详细信息,请参阅 管理对 Azure 存储资源的访问权限。 容器、Blob、队列或表可以通过共享访问签名进行签名访问;共享访问签名是通过不同的机制授权的。 有关更多详细信息 ,请参阅使用共享访问签名委托访问权限

指定 Date 标头

所有授权请求都必须包括请求的协调世界时 (UTC) 时间戳。 可以在 x-ms-date 标头中或在标准 HTTP/HTTPS Date 标头中指定时间戳。 如果在请求中同时指定这两个标头,则使用 x-ms-date 的值作为请求的创建时间。

存储服务确保到请求获得服务时,请求的存在时间不超过 15 分钟。 这能防范某些安全攻击,包括重放攻击。 如果此检查失败,服务器将返回响应码 403(已禁止)。

注意

x-ms-date之所以提供 标头,是因为某些 HTTP 客户端库和代理会自动设置 Date 标头,并且不会为开发人员提供读取其值以将其包含在授权请求中的机会。 如果设置 x-ms-date,则对 Date 标头使用空值来构造签名。

指定 Authorization 标头

授权请求必须包含 Authorization 标头。 如果未包含此标头,则请求是匿名的,并且仅针对标记为公共访问的容器或 Blob,或者针对已为其提供了共享访问签名的容器、Blob、队列或表成功进行委托访问。

若要授权请求,必须使用发出请求的帐户的密钥对请求进行签名,并将该签名作为请求的一部分传递。

Authorization 标头的格式如下所示:

Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"  

其中 SharedKeySharedKeyLite 是授权方案的名称,AccountName 是请求资源的帐户的名称,Signature 是基于散列的消息身份验证代码 (HMAC),该代码基于请求构造,并使用 SHA256 算法计算,然后使用 Base64 编码方式进行编码。

注意

如果驻留在不同帐户下的资源是允许公共访问的资源,则可以请求该资源。

以下几节介绍如何构造 Authorization 标头。

构造签名字符串

如何构造签名字符串取决于要针对哪个服务和版本进行授权,以及所使用的授权方案。 构造签名字符串时,请注意以下几点:

  • 字符串的 VERB 部分是 HTTP 动词,如 GET 或 PUT,必须为大写形式。

  • 对于 Blob、队列和文件服务的共享密钥授权,签名字符串中包含的每个标头只能出现一次。 如果有任何标头重复,则服务将返回状态码 400(错误请求)。

  • 所有标准 HTTP 标头的值都必须按照签名格式中所示顺序包括在字符串中,且不含头名称。 如果未在请求中指定这些标头,则这些标头可以为空;在这种情况下,只需要换行字符。

  • 如果指定了 x-ms-date 标头,则可以忽略 Date 标头(无论是否在请求中指定),只需为签名字符串的部分 Date 指定一个空行即可。 在这种情况下,请按照 构造规范化标头字符串 部分中的说明添加 x-ms-date 标头。

    可以同时 x-ms-date 指定 和 Date;在这种情况下,服务使用 的值 x-ms-date

  • 如果未指定 x-ms-date 标头,请在签名字符串中指定 Date 标头,且不指定标头名称。

  • 签名字符串中必须包含显示的所有换行符 (\n)。

  • 签名字符串包括规范化标头和规范化资源字符串。 规范化这些字符串就是将它们转换为 Azure 存储可以识别的标准格式。 有关构造组成签名字符串的 CanonicalizedHeadersCanonicalizedResource 字符串的详细信息,请参见本主题中后面对应的小节。

Blob、队列和文件服务 (共享密钥授权)

若要针对 2009-09-19 版本及更高版本的 Blob 或队列服务请求以及 2014-02-14 版本及更高版本的文件服务请求为共享密钥签名字符串进行编码,请采用以下格式:

StringToSign = VERB + "\n" +  
               Content-Encoding + "\n" +  
               Content-Language + "\n" +  
               Content-Length + "\n" +  
               Content-MD5 + "\n" +  
               Content-Type + "\n" +  
               Date + "\n" +  
               If-Modified-Since + "\n" +  
               If-Match + "\n" +  
               If-None-Match + "\n" +  
               If-Unmodified-Since + "\n" +  
               Range + "\n" +  
               CanonicalizedHeaders +   
               CanonicalizedResource;  

重要

在当前版本中,如果请求的内容长度为零,则 Content-Length 字段必须为空字符串。 在版本 2014-02-14 及更早版本中,即使为零,也包含内容长度。 有关旧行为的详细信息,请参阅下文。

以下示例演示 获取 Blob 操作的签名字符串。 如果没有标头值,则仅指定新行字符。

GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Fri, 26 Jun 2015 23:39:12 GMT\nx-ms-version:2015-02-21\n/myaccount/mycontainer\ncomp:metadata\nrestype:container\ntimeout:20  

逐行拆分将显示相同字符串的每一个部分:

GET\n /*HTTP Verb*/  
\n    /*Content-Encoding*/  
\n    /*Content-Language*/  
\n    /*Content-Length (empty string when zero)*/  
\n    /*Content-MD5*/  
\n    /*Content-Type*/  
\n    /*Date*/  
\n    /*If-Modified-Since */  
\n    /*If-Match*/  
\n    /*If-None-Match*/  
\n    /*If-Unmodified-Since*/  
\n    /*Range*/  
x-ms-date:Fri, 26 Jun 2015 23:39:12 GMT\nx-ms-version:2015-02-21\n    /*CanonicalizedHeaders*/  
/myaccount /mycontainer\ncomp:metadata\nrestype:container\ntimeout:20    /*CanonicalizedResource*/  

接下来,对采用 UTF-8 编码的签名字符串使用 HMAC-SHA256 算法进行编码,构造 Authorization 标头,并将该标头添加到请求。 下面的示例是用于相同操作的 Authorization 标头:

Authorization: SharedKey myaccount:ctzMq410TV3wS7upTBcunJTDLEJwMAZuFPfr0mrrA08=  

若要对 Blob 和队列服务版本 2009-09-19 及更高版本使用共享密钥授权,必须更新代码以使用此扩充签名字符串。

如果希望通过尽可能少的更改将代码迁移到 Blob 和队列服务的 2009-09-19 或更高版本,则可以修改现有 Authorization 标头以使用共享密钥 Lite 而不是共享密钥。 共享密钥 Lite 要求的签名格式与 2009-09-19 之前版本的 Blob 和队列服务的共享密钥要求的格式相同。

重要

如果要访问存储帐户中已启用读访问地域复制 (RA-GRS) 的辅助位置,请不要在 authorization 标头中包括 -secondary 指定。 出于授权目的,帐户名称始终是主位置的名称,甚至对于辅助访问也是如此。

版本 2014-02-14 及更早版本中的内容长度标头

使用版本 2014-02-14 或更早版本时,如果 Content-Length 为零,则将 部分StringToSign设置为 Content-Length0。 通常,这是一个空字符串。

例如,对于以下请求,即使标头为 Content-Length 零,标头的值也会包含在 中 StringToSign

PUT http://myaccount/mycontainer?restype=container&timeout=30 HTTP/1.1  
x-ms-version: 2014-02-14  
x-ms-date: Fri, 26 Jun 2015 23:39:12 GMT  
Authorization: SharedKey myaccount:ctzMq410TV3wS7upTBcunJTDLEJwMAZuFPfr0mrrA08=  
Content-Length: 0

StringToSign构造如下:

Version 2014-02-14 and earlier:
PUT\n\n\n\n0\n\n\n\n\n\n\n\nx-ms-date:Fri, 26 Jun 2015 23:39:12 GMT\nx-ms-version:2014-02-14\n/myaccount/mycontainer\nrestype:container\ntimeout:30

而在 2014-02-14 之后的版本中, StringToSign 必须包含 的 Content-Length空字符串:

Version 2015-02-21 and later:
PUT\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Fri, 26 Jun 2015 23:39:12 GMT\nx-ms-version:2015-02-21\n/myaccount/mycontainer\nrestype:container\ntimeout:30

表服务 (共享密钥授权)

如果服务使用 REST API 发出请求,则必须使用共享密钥授权来授权针对表服务发出的请求。 表服务共享密钥的签名字符串的格式对所有版本都相同。

针对表服务的请求的共享密钥签名字符串与针对 Blob 或队列服务的请求的共享密钥签名字符串略有不同,因为它不包括 CanonicalizedHeaders 字符串的 部分。 此外,这种情况下,Date 标头不会为空,即使请求设置了 x-ms-date 标头也不例外。 如果请求设置 x-ms-date,则也会使用该值作为 Date 标头值。

要对使用 REST API 的表服务请求的签名字符串进行编码,请使用以下格式:

StringToSign = VERB + "\n" +
               Content-MD5 + "\n" +
               Content-Type + "\n" +  
               Date + "\n" +  
               CanonicalizedResource;  

注意

从版本 2009-09-19 开始,表服务要求所有 REST 调用都包括 DataServiceVersionMaxDataServiceVersion 标头。 有关详细信息 ,请参阅设置 OData 数据服务版本标头

Blob、队列和文件服务 (共享密钥精简授权)

可以使用共享密钥精简授权来授权针对 Blob 和队列服务的 2009-09-19 及更高版本以及文件服务 2014-02-14 及更高版本发出的请求。

共享密钥精简版的签名字符串与 2009-09-19 之前的 Blob 和队列服务版本中的共享密钥授权所需的签名字符串相同。 因此,如果要迁移对 Blob 和队列服务版本 2009-09-19 进行最少更改的代码,则可以修改代码以使用共享密钥精简版,而无需更改签名字符串本身。 通过使用共享密钥精简版,你将不会获得使用版本 2009-09-19 及更高版本的共享密钥提供的增强安全功能。

要对用于 Blob 或队列服务请求的签名字符串进行编码,请使用以下格式:

StringToSign = VERB + "\n" +  
               Content-MD5 + "\n" +  
               Content-Type + "\n" +  
               Date + "\n" +  
               CanonicalizedHeaders +   
               CanonicalizedResource;  

以下示例演示 Put Blob 操作的签名字符串。 请注意,Content-MD5 标头行为空。 显示在该字符串中的标头是指定新 Blob 的自定义元数据值的名称/值对。

PUT\n\ntext/plain; charset=UTF-8\n\nx-ms-date:Sun, 20 Sep 2009 20:36:40 GMT\nx-ms-meta-m1:v1\nx-ms-meta-m2:v2\n/testaccount1/mycontainer/hello.txt  

接下来,对采用 UTF-8 编码的签名字符串使用 HMAC-SHA256 算法进行编码,构造 Authorization 标头,并将该标头添加到请求。 下面的示例是用于相同操作的 Authorization 标头:

Authorization: SharedKeyLite myaccount:ctzMq410TV3wS7upTBcunJTDLEJwMAZuFPfr0mrrA08=  

表服务 (共享密钥精简授权)

可以使用共享密钥精简授权来授权针对任何表服务版本发出的请求。

要对使用共享密钥 Lite 的表服务请求的签名字符串进行编码,请使用以下格式:

StringToSign = Date + "\n"
               CanonicalizedResource  

以下示例演示Create表操作的签名字符串。

Sun, 11 Oct 2009 19:52:39 GMT\n/testaccount1/Tables  

接下来,使用 HMAC-SHA256 算法对此字符串进行编码,构造 Authorization 标头,然后将该标头添加到请求。 下面的示例是用于相同操作的 Authorization 标头:

Authorization: SharedKeyLite testaccount1:uay+rilMVayH/SVI8X+a3fL8k/NxCnIePdyZSkqvydM=  

构造规范化标头字符串

要构造签名字符串的 CanonicalizedHeaders 部分,请执行以下步骤:

  1. 检索以 x-ms- 开头的所有资源标头,包括 x-ms-date 标头。

  2. 将每个 HTTP 标头名称转换为小写形式。

  3. 以升序基于标头名称按字母顺序对标头进行排序。 每个标头在字符串中只能出现一次。

    注意

    字典排序 可能并不总是与传统的字母顺序一致。

  4. 将标头值中的任何线性空格替换为单个空格。

线性空格包括回车符/换行符 (CRLF) 、空格和制表符。 有关详细信息 ,请参阅 RFC 2616 第 4.2 节 。 请勿替换带引号的字符串中的任何空格。

  1. 剪裁标头中冒号周围的任何空格。

  2. 最后,在产生的列表中的每一个规范化标头之后追加一个换行符。 将此列表中的所有标头连接起来成为一个字符串,构造 CanonicalizedHeaders 字符串。

下面演示了规范化标头字符串的示例:

x-ms-date:Sat, 21 Feb 2015 00:48:38 GMT\nx-ms-version:2014-02-14\n

注意

在服务版本 2016-05-31 之前,签名字符串中省略了具有空值的标头。 这些现在在 CanonicalizedHeaders 中表示,方法是紧跟在冒号字符后加上终止新行。

构造规范化资源字符串

签名字符串的 CanonicalizedResource 部分表示要请求的存储服务资源。 源自资源 URI 的CanonicalizedResource 字符串的任何部分都应该安全按照其在 URI 中的形式进行编码。

对于 CanonicalizedResource 字符串,支持以下两种格式:

  • 支持 Blob 和队列服务版本 2009-09-19 及更高版本以及文件服务版本 2014-02-14 及更高版本的共享密钥授权的格式。

  • 一种格式支持用于所有版本表服务的共享密钥和共享密钥 Lite,以及用于 2009-09-19 版本及更高版本 Blob 和队列服务的共享密钥 Lite。 此格式与用于先前版本的存储服务的格式相同。

有关为要访问的资源构造 URI 的帮助,请参见以下主题之一:

重要

如果你使用读取访问地域复制 (RA-GRS) 复制存储帐户,并且要访问辅助位置中的资源,请不要在 –secondary 字符串中包括 CanonicalizedResource 指定。 CanonicalizedResource 字符串 URI 中使用的资源 URI 应是主位置资源的 URI。

注意

如果要针对存储模拟器授权,帐户名称将在字符串中 CanonicalizedResource 显示两次。 这是正常情况。 如果要对 Azure 存储服务授权,则帐户名称只会在字符串中 CanonicalizedResource 显示一次。

2009-09-19 及更高版本的共享密钥格式

此格式支持 Blob 和队列服务的 2009-09-19 及更高版本以及文件服务的 2014-02-14 及更高版本共享密钥授权。 按照如下格式构造 CanonicalizedResource 字符串:

  1. 以空字符串 ("") 开头,随后追加一个正斜杠 (/),再接拥有待访问资源的帐户的名称。

  2. 不带任何查询参数,附加该资源的编码 URI 路径。

  3. 检索资源 URI 的所有查询参数,包括 comp 参数(如果有)。

  4. 将所有参数名称转换为小写形式。

  5. 以升序基于参数名称按字母顺序对查询参数进行排序。

  6. 对每个查询参数名称和值进行 URL 解码。

  7. 在每个名称/值对之前 (\n) 包含一个新行字符。

  8. 按以下格式在该字符串之后附加每个查询参数名称和值,并确保在名称与值之间使用冒号 (:):

    parameter-name:parameter-value

  9. 如果一个查询参数具有多个值,请按字母顺序将所有值进行排序,然后以逗号分隔列表的形式包括这些值:

    parameter-name:parameter-value-1,parameter-value-2,parameter-value-n

记住以下规则以构造规范化资源字符串:

  • 避免在查询参数的值中使用换行符 (\n)。 如果必须使用换行符,请确保它不会影响规范化资源字符串的格式。

  • 避免在查询参数值中使用逗号。

下面是一些显示 CanonicalizedResource 签名字符串部分的示例,因为它可以从给定的请求 URI 构造:

Get Container Metadata  
   GET http://myaccount.blob.core.windows.net/mycontainer?restype=container&comp=metadata
CanonicalizedResource:  
    /myaccount/mycontainer\ncomp:metadata\nrestype:container  
  
List Blobs operation:  
    GET http://myaccount.blob.core.windows.net/container?restype=container&comp=list&include=snapshots&include=metadata&include=uncommittedblobs  
CanonicalizedResource:  
    /myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,uncommittedblobs\nrestype:container  
  
Get Blob operation against a resource in the secondary location:  
   GET https://myaccount-secondary.blob.core.windows.net/mycontainer/myblob  
CanonicalizedResource:  
    /myaccount/mycontainer/myblob

2009-09-19 及更高版本的共享密钥精简版和表服务格式

对于所有版本的表服务,此格式支持共享密钥和共享密钥 Lite;对于 2009-09-19 版本及更高版本的 Blob 和队列服务以及 2014-02-14 版本及更高版本的文件服务,此格式支持共享密钥 Lite。 此格式与用于先前版本的存储服务的格式相同。 按照如下格式构造 CanonicalizedResource 字符串:

  1. 以空字符串 ("") 开头,随后追加一个正斜杠 (/),再接拥有待访问资源的帐户的名称。

  2. 附加资源的编码 URI 路径。 如果请求 URI 找到了资源的一个组件,则附加相应的查询字符串。 该查询字符串应该包括问号和 comp 参数(例如 ?comp=metadata)。 查询字符串中不应该包括其他任何参数。

对签名进行编码

要对签名进行编码,请对 UTF-8 编码的签名字符串调用 HMAC-SHA256 算法,并将结果编码为 Base64 格式。 请注意,还需要对存储帐户密钥进行 Base64 解码。 使用以下格式(显示为伪代码):

Signature=Base64(HMAC-SHA256(UTF8(StringToSign), Base64.decode(<your_azure_storage_account_shared_key>)))  

另请参阅