Azure BLOB PUT Rest api - 无法通过 Python 进行身份验证
Azure BLOB PUT Rest api - Not able to authenticate with Python
我正在尝试使用 Python 和 Azure Blob Rest API 在 Azure 容器中创建一个 Blob。这是一个有趣的练习,因为这是我第一次与 Azure Rest APIs 交互。我已经阅读了有关它的 MS 文档,还阅读了该站点中的许多问题,根据它们,我的代码似乎是正确的,但是我还不能成功地进行 PUT。我能够执行 GET 请求(列表 containers/blobs)。
代码如下:
import requests
import datetime
import hmac
import hashlib
import base64
storage_account_name = '<mystorageaccount>'
storage_account_key = '<mystoragekey>'
container_name='test'
api_version = '2015-02-21'
request_time = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
string_params = {
'verb': 'PUT',
'Content-Encoding': '',
'Content-Language': '',
'Content-Length': '11',
'Content-MD5': '',
'Content-Type': 'text/plain; charset=UTF-8',
'Date': '',
'If-Modified-Since': '',
'If-Match': '',
'If-None-Match': '',
'If-Unmodified-Since': '',
'Range': '',
'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob' + '\nx-ms-date:' + request_time + '\nx-ms-version:' + api_version,
'CanonicalizedResource': '/' + storage_account_name +'/'+container_name+ '/' +'fname'
}
string_to_sign = (string_params['verb'] + '\n'
+ string_params['Content-Encoding'] + '\n'
+ string_params['Content-Language'] + '\n'
+ string_params['Content-Length'] + '\n'
+ string_params['Content-MD5'] + '\n'
+ string_params['Content-Type'] + '\n'
+ string_params['Date'] + '\n'
+ string_params['If-Modified-Since'] + '\n'
+ string_params['If-Match'] + '\n'
+ string_params['If-None-Match'] + '\n'
+ string_params['If-Unmodified-Since'] + '\n'
+ string_params['Range'] + '\n'
+ string_params['CanonicalizedHeaders']
+ string_params['CanonicalizedResource'])
signed_string = base64.b64encode(hmac.new(base64.b64decode(storage_account_key), msg=string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()).decode()
headers = {
'x-ms-version' : api_version,
'x-ms-date' : request_time,
'x-ms-blob-type': 'BlockBlob',
'Content-Length': '11',
'Content-Type': "text/plain; charset=UTF-8",
'Authorization' : ('SharedKey ' + storage_account_name + ':' + signed_string)
}
url = ('https://' + storage_account_name + '.blob.core.windows.net/'+container_name+'/fname')
r = requests.put(url, headers = headers,data='hello world')
print(r.status_code)
print('\n\n'+r.text)
那是我收到的 return 错误信息:
<?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.
RequestId:cbf12c65-c01e-00fc-1069-3a41a7000000
Time:2020-06-04T12:11:03.4295368Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request 'c9n6EKq9p6skUs17qGv/bW0yGRGjMzMrP7bgDwjRABg=' is not the same as any computed signature. Server used following string to sign: 'PUT
11
text/plain; charset=UTF-8
x-ms-blob-type:BlockBlob
x-ms-date:Thu, 04 Jun 2020 12:11:02 GMT
x-ms-version:2015-02-21
/<mystorageaccount>/test/fname'.</AuthenticationErrorDetail></Error>
有人可以帮我理解我在这里遗漏了什么吗?
您需要在 string_params
中为 Content-Type
添加 text/plain; charset=UTF-8
,为 Content-Length
添加 11
。因此,您修改后的代码(仅 string_params
变量)将类似于:
string_params = {
'verb': 'PUT',
'Content-Encoding': '',
'Content-Language': '',
'Content-Length': '11',
'Content-MD5': '',
'Content-Type': 'text/plain; charset=UTF-8',
'Date': '',
'If-Modified-Since': '',
'If-Match': '',
'If-None-Match': '',
'If-Unmodified-Since': '',
'Range': '',
'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob' + '\nx-ms-date:' + request_time + '\nx-ms-version:' + api_version,
'CanonicalizedResource': '/' + storage_account_name +'/'+container_name+ '/' +'fname'
}
您的其余代码看起来不错。
更新
您在 CanonicalizedHeaders
末尾缺少换行符。所以你的 string_params
会像:
string_params = {
'verb': 'PUT',
'Content-Encoding': '',
'Content-Language': '',
'Content-Length': '11',
'Content-MD5': '',
'Content-Type': 'text/plain; charset=UTF-8',
'Date': '',
'If-Modified-Since': '',
'If-Match': '',
'If-None-Match': '',
'If-Unmodified-Since': '',
'Range': '',
'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob' + '\nx-ms-date:' + request_time + '\nx-ms-version:' + api_version + '\n',
'CanonicalizedResource': '/' + storage_account_name +'/'+container_name+ '/' +'fname'
}
这里是代码重用更多的更简单版本:
def generate_key(verb, headers, content_type, storage_account_key, canonicalized_resource):
string_to_sign = (verb + '\n'
+ '\n' # Content-MD5
+ content_type+ '\n'
+ '\n' #Date
+ "\n".join([k + ":" + v for k,v in headers.items()]) + '\n'
+ canonicalized_resource)
return base64.b64encode(
hmac.new(base64.b64decode(storage_account_key), msg=string_to_sign.encode('utf-8'),
digestmod=hashlib.sha256).digest()).decode()
def request_with_headers(verb, storage_account_name, storage_account_key, container_name, blob_name, data = None):
api_version = '2018-11-09'
request_time = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
headers = {"x-ms-blob-type": "BlockBlob", "x-ms-date": request_time, "x-ms-version": api_version}
canonicalized_resource = '/' + storage_account_name + '/' + container_name + '/' + blob_name
content_type = "text/plain; charset=UTF-8"
sas_token = generate_key(verb, headers, content_type, storage_account_key, canonicalized_resource)
headers = dict(headers, **{"Content-Type": content_type, "Authorization": "SharedKeyLite %s:%s" % (storage_account_name, sas_token)})
url = "https://%s.blob.core.windows.net/%s/%s" % (storage_account_name, container_name, blob_name)
return requests.request(verb, url, headers=headers, data = data)
我正在尝试使用 Python 和 Azure Blob Rest API 在 Azure 容器中创建一个 Blob。这是一个有趣的练习,因为这是我第一次与 Azure Rest APIs 交互。我已经阅读了有关它的 MS 文档,还阅读了该站点中的许多问题,根据它们,我的代码似乎是正确的,但是我还不能成功地进行 PUT。我能够执行 GET 请求(列表 containers/blobs)。
代码如下:
import requests
import datetime
import hmac
import hashlib
import base64
storage_account_name = '<mystorageaccount>'
storage_account_key = '<mystoragekey>'
container_name='test'
api_version = '2015-02-21'
request_time = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
string_params = {
'verb': 'PUT',
'Content-Encoding': '',
'Content-Language': '',
'Content-Length': '11',
'Content-MD5': '',
'Content-Type': 'text/plain; charset=UTF-8',
'Date': '',
'If-Modified-Since': '',
'If-Match': '',
'If-None-Match': '',
'If-Unmodified-Since': '',
'Range': '',
'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob' + '\nx-ms-date:' + request_time + '\nx-ms-version:' + api_version,
'CanonicalizedResource': '/' + storage_account_name +'/'+container_name+ '/' +'fname'
}
string_to_sign = (string_params['verb'] + '\n'
+ string_params['Content-Encoding'] + '\n'
+ string_params['Content-Language'] + '\n'
+ string_params['Content-Length'] + '\n'
+ string_params['Content-MD5'] + '\n'
+ string_params['Content-Type'] + '\n'
+ string_params['Date'] + '\n'
+ string_params['If-Modified-Since'] + '\n'
+ string_params['If-Match'] + '\n'
+ string_params['If-None-Match'] + '\n'
+ string_params['If-Unmodified-Since'] + '\n'
+ string_params['Range'] + '\n'
+ string_params['CanonicalizedHeaders']
+ string_params['CanonicalizedResource'])
signed_string = base64.b64encode(hmac.new(base64.b64decode(storage_account_key), msg=string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()).decode()
headers = {
'x-ms-version' : api_version,
'x-ms-date' : request_time,
'x-ms-blob-type': 'BlockBlob',
'Content-Length': '11',
'Content-Type': "text/plain; charset=UTF-8",
'Authorization' : ('SharedKey ' + storage_account_name + ':' + signed_string)
}
url = ('https://' + storage_account_name + '.blob.core.windows.net/'+container_name+'/fname')
r = requests.put(url, headers = headers,data='hello world')
print(r.status_code)
print('\n\n'+r.text)
那是我收到的 return 错误信息:
<?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.
RequestId:cbf12c65-c01e-00fc-1069-3a41a7000000
Time:2020-06-04T12:11:03.4295368Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request 'c9n6EKq9p6skUs17qGv/bW0yGRGjMzMrP7bgDwjRABg=' is not the same as any computed signature. Server used following string to sign: 'PUT
11
text/plain; charset=UTF-8
x-ms-blob-type:BlockBlob
x-ms-date:Thu, 04 Jun 2020 12:11:02 GMT
x-ms-version:2015-02-21
/<mystorageaccount>/test/fname'.</AuthenticationErrorDetail></Error>
有人可以帮我理解我在这里遗漏了什么吗?
您需要在 string_params
中为 Content-Type
添加 text/plain; charset=UTF-8
,为 Content-Length
添加 11
。因此,您修改后的代码(仅 string_params
变量)将类似于:
string_params = {
'verb': 'PUT',
'Content-Encoding': '',
'Content-Language': '',
'Content-Length': '11',
'Content-MD5': '',
'Content-Type': 'text/plain; charset=UTF-8',
'Date': '',
'If-Modified-Since': '',
'If-Match': '',
'If-None-Match': '',
'If-Unmodified-Since': '',
'Range': '',
'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob' + '\nx-ms-date:' + request_time + '\nx-ms-version:' + api_version,
'CanonicalizedResource': '/' + storage_account_name +'/'+container_name+ '/' +'fname'
}
您的其余代码看起来不错。
更新
您在 CanonicalizedHeaders
末尾缺少换行符。所以你的 string_params
会像:
string_params = {
'verb': 'PUT',
'Content-Encoding': '',
'Content-Language': '',
'Content-Length': '11',
'Content-MD5': '',
'Content-Type': 'text/plain; charset=UTF-8',
'Date': '',
'If-Modified-Since': '',
'If-Match': '',
'If-None-Match': '',
'If-Unmodified-Since': '',
'Range': '',
'CanonicalizedHeaders': 'x-ms-blob-type:BlockBlob' + '\nx-ms-date:' + request_time + '\nx-ms-version:' + api_version + '\n',
'CanonicalizedResource': '/' + storage_account_name +'/'+container_name+ '/' +'fname'
}
这里是代码重用更多的更简单版本:
def generate_key(verb, headers, content_type, storage_account_key, canonicalized_resource):
string_to_sign = (verb + '\n'
+ '\n' # Content-MD5
+ content_type+ '\n'
+ '\n' #Date
+ "\n".join([k + ":" + v for k,v in headers.items()]) + '\n'
+ canonicalized_resource)
return base64.b64encode(
hmac.new(base64.b64decode(storage_account_key), msg=string_to_sign.encode('utf-8'),
digestmod=hashlib.sha256).digest()).decode()
def request_with_headers(verb, storage_account_name, storage_account_key, container_name, blob_name, data = None):
api_version = '2018-11-09'
request_time = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
headers = {"x-ms-blob-type": "BlockBlob", "x-ms-date": request_time, "x-ms-version": api_version}
canonicalized_resource = '/' + storage_account_name + '/' + container_name + '/' + blob_name
content_type = "text/plain; charset=UTF-8"
sas_token = generate_key(verb, headers, content_type, storage_account_key, canonicalized_resource)
headers = dict(headers, **{"Content-Type": content_type, "Authorization": "SharedKeyLite %s:%s" % (storage_account_name, sas_token)})
url = "https://%s.blob.core.windows.net/%s/%s" % (storage_account_name, container_name, blob_name)
return requests.request(verb, url, headers=headers, data = data)