你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
通过共享密钥进行授权
对存储服务发出的每个请求都必须获得授权,除非请求针对已可供公共或已签名访问的 Blob 或容器资源。 授权请求的一个选项是使用共享密钥,如本文中所述。
提示
Azure 存储提供与Microsoft Entra ID的集成,用于对 Blob、文件、队列和表服务的请求进行基于标识的授权。 使用 Microsoft Entra ID,可以使用基于角色的访问控制 (RBAC) 向用户、组或应用程序授予对 Blob、文件、队列和表资源的访问权限。 可以使用Microsoft Entra ID授权访问存储资源,而无需在应用程序中存储帐户访问密钥,就像使用共享密钥一样。 有关详细信息,请参阅使用Microsoft Entra ID授权。
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 和队列服务,而无需更新签名字符串。
授权请求需要两个标头: Date
或 x-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>"
其中 SharedKey
或 SharedKeyLite
是授权方案的名称,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 存储可以识别的标准格式。 有关构造组成签名字符串的
CanonicalizedHeaders
和CanonicalizedResource
字符串的详细信息,请参见本主题中后面对应的小节。
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 及更早版本中,即使为零,也包括内容长度。 有关旧行为的详细信息,请参阅下文。
以下示例演示 Get 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 要求的签名格式与 2009-09-19 之前版本的 Blob 和队列服务的共享密钥要求的格式相同。
重要
如果要访问存储帐户中已启用读访问地域复制 (RA-GRS) 的辅助位置,请不要在 authorization 标头中包括 -secondary
指定。 出于授权目的,帐户名称始终是主位置的名称,甚至对于辅助访问也是如此。
版本 2014-02-14 及更早版本中的内容长度标头
使用版本 2014-02-14 或更早版本时,如果 Content-Length
为零,则将 部分StringToSign
设置为 Content-Length
0
。 通常,这将是一个空字符串。
例如,对于以下请求,即使标头为零, 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 调用都包括 DataServiceVersion
和 MaxDataServiceVersion
标头。 有关详细信息 ,请参阅设置 OData 数据服务版本标头 。
Blob、队列和文件服务 (共享密钥精简授权)
可以使用共享密钥精简授权来授权针对 Blob 和队列服务的 2009-09-19 及更高版本以及文件服务 2014-02-14 及更高版本发出的请求。
共享密钥精简版的签名字符串与 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 的表服务请求的签名字符串进行编码,请使用以下格式:
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
部分,请执行以下步骤:
检索以
x-ms-
开头的所有资源标头,包括x-ms-date
标头。将每个 HTTP 标头名称转换为小写形式。
以升序基于标头名称按字母顺序对标头进行排序。 每个标头在字符串中只能出现一次。
注意
字典排序 可能并不总是与传统的字母顺序一致。
将标头值中的任何线性空格替换为单个空格。
线性空格包括回车符/换行符 (CRLF) 、空格和制表符。 有关详细信息 ,请参阅 RFC 2616 第 4.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 的帮助,请参见以下主题之一:
Blob 服务: 命名和引用容器、Blob 和元数据
队列服务: 对队列服务资源进行寻址
表服务: 对表服务资源进行寻址
文件服务: 命名和引用共享、目录、文件和元数据
重要
如果你使用读取访问地域复制 (RA-GRS) 复制存储帐户,并且要访问辅助位置中的资源,请不要在 –secondary
字符串中包括 CanonicalizedResource
指定。 CanonicalizedResource
字符串 URI 中使用的资源 URI 应是主位置资源的 URI。
注意
如果要针对存储模拟器授权,帐户名称将在字符串中 CanonicalizedResource
出现两次。 这是正常情况。 如果要对 Azure 存储服务授权,帐户名称只会在字符串中 CanonicalizedResource
出现一次。
2009-09-19 及更高版本的共享密钥格式
此格式支持 Blob 和队列服务的 2009-09-19 及更高版本以及文件服务的 2014-02-14 版本及更高版本的共享密钥授权。 按照如下格式构造 CanonicalizedResource
字符串:
以空字符串 ("") 开头,随后追加一个正斜杠 (/),再接拥有待访问资源的帐户的名称。
不带任何查询参数,附加该资源的编码 URI 路径。
检索资源 URI 的所有查询参数,包括
comp
参数(如果有)。将所有参数名称转换为小写形式。
以升序基于参数名称按字母顺序对查询参数进行排序。
对每个查询参数名称和值进行 URL 解码。
在每个名称/值对之前 (\n) 包含一个新行字符。
按以下格式在该字符串之后附加每个查询参数名称和值,并确保在名称与值之间使用冒号 (:):
parameter-name:parameter-value
如果一个查询参数具有多个值,请按字母顺序将所有值进行排序,然后以逗号分隔列表的形式包括这些值:
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
字符串:
以空字符串 ("") 开头,随后追加一个正斜杠 (/),再接拥有待访问资源的帐户的名称。
附加资源的编码 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>)))