【Azure 存储服务】记一次调用Storage Blob API使用 SharedKey Authorization出现的403错误

发布时间 2023-06-29 21:25:16作者: 路边两盏灯

问题描述

使用Azure Storag Blob REST API上传文件,用SharedKey作为Authorization出现403错误。

错误消息

b'\xef\xbb\xbf<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:7df1f139-901e-0063-64eb-4be2dc000000\nTime:2023-03-01T03:08:27.3277224Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request \’*\' is not the same as any computed signature. Server used following string to sign: \'PUT\n\n\n19122\n\ntext/plain; charset=UTF-8\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:Wed, 01 Mar 2023 03:08:26 GMT\nx-ms-version:2015-02-21\n/adlstestaccount/blobtest/20230220065800824-2696.jpg\'.</AuthenticationErrorDetail></Error>' reason=Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.

 

问题发现

在错误消息“Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.”中,提到了请求SharedKey 中的内容和请求Header中的内容不一致。 

在检查生成SharedKey 的代码中发现

put_str = u"PUT\n\n\n{}\n\n{}; charset={}\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:{}\nx-ms-version:{}\n/{}/{}/{}".format(str(len(file)),
'text/plain' ,
'UTF-8',
get_date(),
'2015-02-21',
'<your storage account name>',
'<container name>',
file_name) sig_str
= base64.b64encode(hmac.new(account_key_decoded, put_str, digestmod=hashlib.sha256).digest()).decode('utf-8')

然后再请求的Header中,通过Authorization 传递 sig_str

        headers = {
            "x-ms-blob-type": "BlockBlob",
            "x-ms-date": get_date(),
            "x-ms-version": "2015-02-21",
            "Authorization": "SharedKey {}:{}".format("adlstestaccount", sig_str),
            "Content-Type": "{}; charset={}".format('text/plain', 'UTF-8'),
            "Content-Length": str(len(file))
        }

在调试代码的过程中,对比 put_str 内容和 blob服务端 SharedKey信息,发现它们的 x-ms-date 部分值不一样:

因为SharedKey不一样,所以认证时候出现403 Server failed to authenticate the request. 

因为在第一步生成 put_str 的时候获取 x-ms-date 的时候调用的 get_date() 获取时间,第二步在生成Header x-ms-date的时候再一次调用 get_date() 获取时间,由于代码在执行时候出现了时间差,这就导致了header中的时间和Authorization SharedKey中时间不一致。这就是出现403的根本原因。

解决办法就是在获取x-ms-date的时候,不要两次调用get_date()方法,而是只调用一次,然后put_str和header中都使用同一个时间值。

修改后的代码片段为

//获取时间
ms_date = get_date();

put_str = u"PUT\n\n\n{}\n\n{}; charset={}\n\n\n\n\n\n\nx-ms-blob-type:BlockBlob\nx-ms-date:{}\nx-ms-version:{}\n/{}/{}/{}".format(str(len(file)),
'text/plain' ,
'UTF-8',
ms_date,
'2015-02-21',
'<your storage account name>',
'<container name>',
file_name)

sig_str = base64.b64encode(hmac.new(account_key_decoded, put_str, digestmod=hashlib.sha256).digest()).decode('utf-8')


headers = {
            "x-ms-blob-type": "BlockBlob",
            "x-ms-date": ms_date,
            "x-ms-version": "2015-02-21",
            "Authorization": "SharedKey {}:{}".format("adlstestaccount", sig_str),
            "Content-Type": "{}; charset={}".format('text/plain', 'UTF-8'),
            "Content-Length": str(len(file))
        }
        

 

附录:使用AAD 和 Storage Account Access Key进行认证获取Blob的Python实例代码

from azure.storage.filedatalake import DataLakeServiceClient
from azure.identity import ClientSecretCredential
from azure.identity import AzureAuthorityHosts

tenant_id = "*"
client_id = "*"
client_secret = "*"
authority = AzureAuthorityHosts.AZURE_CHINA 
credential = ClientSecretCredential(tenant_id=tenant_id, client_id=client_id, client_secret=client_secret,authority=authority) 
account_url = "*"
account_key = "*"
container="*"
folder="*"
connection_string = "*"

def initialize_storage_account():
    try:
        global service_client 
        #使用AAD 认证
        #service_client = DataLakeServiceClient(account_url=account_url, credential=credential)

        #使用Access Key认证
        service_client = DataLakeServiceClient.from_connection_string(connection_string)
    except Exception as e:
         print(e)
         

def list_directory_contents():
    try:
        file_system_client = service_client.get_file_system_client(file_system=container)
        paths = file_system_client.get_paths(path=folder)
        for path in paths:
         print(path.name + '\n')
    except Exception as e:
     print(e)

initialize_storage_account()
list_directory_contents()
    

 

 

 

参考资料

通过共享密钥进行授权 : https://learn.microsoft.com/zh-cn/rest/api/storageservices/authorize-with-shared-key 

Azure Blob 存储和 Python 入门 : https://learn.microsoft.com/zh-cn/azure/storage/blobs/storage-blob-python-get-started?tabs=azure-ad