你当前正在访问 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 凭据而不是帐户密钥进行保护。 若要了解共享访问签名,请参阅 创建用户委派 SAS

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

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

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

  • 共享密钥 Lite。 使用共享密钥 Lite 授权方案针对 Blob、队列、表和文件服务发出请求。 高级页 Blob 不支持共享密钥 Lite。

    对于 Blob 和队列服务版本 2009-09-19 及更高版本,共享密钥 Lite 授权支持使用与 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 标头。 如果未包含此标头,则请求是匿名的,并且仅针对标记为公共访问的容器或 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-dateDate;在本例中,服务使用 x-ms-date的值。

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

  • 签名字符串中需要显示的所有新行字符(\n)。

  • 签名字符串包括规范化标头和规范化资源字符串。 规范化这些字符串会将这些字符串置于 Azure 存储识别的标准格式。 有关构造构成签名字符串部分的 CanonicalizedHeadersCanonicalizedResource 字符串的详细信息,请参阅本主题后面的相应部分。

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

若要针对 Blob 或队列服务的 2009-09-19 版本及更高版本以及文件服务版本 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=  

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

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

重要

如果要访问启用了读取访问异地复制(RA-GRS)的存储帐户中的辅助位置,请不要在授权标头中包含 -secondary 指定。 出于授权目的,帐户名称始终是主要位置的名称,即使用于辅助访问也是如此。

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

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

例如,对于以下请求,即使 StringToSign 为零,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 部分。 此外,在这种情况下,即使请求设置 x-ms-date 标头,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、队列和文件服务(共享密钥 Lite 授权)

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

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

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

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

以下示例演示 放置 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 授权)

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

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

StringToSign = Date + "\n"
               CanonicalizedResource  

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

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 的格式,以及 Blob 和队列服务版本 2009-09-19 及更高版本的共享密钥 Lite。 此格式与与以前版本的存储服务一起使用的格式相同。

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

重要

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

注意

如果要针对存储模拟器进行授权,帐户名称将在 CanonicalizedResource 字符串中显示两次。 这是预期的。 如果要针对 Azure 存储服务进行授权,帐户名称将在 CanonicalizedResource 字符串中只显示一次。

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

此格式支持 2009-09-19 版本及更高版本的 Blob 和队列服务的共享密钥授权,以及 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 和表服务格式

此格式支持表服务的所有版本的共享密钥和共享密钥 Lite,以及 Blob 和队列服务版本 2009-09-19 及更高版本的共享密钥 Lite 以及文件服务版本 2014-02-14 及更高版本。 此格式与与以前版本的存储服务一起使用的格式相同。 按以下格式构造 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>)))  

另请参阅