如何在Python中为Azure Blob存储SAS令牌生成签名?

8
我想在Python中构建用于Blob下载URL的SAS令牌,按照MSDN的说明进行操作。
我的签名字符串如下:
r\n
2016-12-22T14%3A00%3A00Z\n
2016-12-22T15%3A00%3A00Z\n
%2Fblob%2Fmytest%2Fprivatefiles%2F1%2Fqux.txt\n
\n
\n
https\n
2015-12-11\n
\n
\n
\n
\n
_

我已经添加了换行符以使内容更加清晰,最后一行应该是一个空行(没有换行符)。
我使用的Python方法来签名字符串是:
def sign(self, string):
    hashed = hmac.new(base64.b64decode(self.account_key), digestmod=sha256)
    hashed.update(string)
    base64_str = base64.encodestring(hashed.digest()).strip()
    return base64_str

我构建的最终URL看起来像:

https://mytest.blob.core.windows.net/privatefiles/1/qux.txt?sv=2015-12-11&st=2016-12-22T14%3A00%3A00Z&se=2016-12-22T15%3A00%3A00Z&sr=b&sp=r&spr=https&sig=BxkcpoRq3xanEHwU6u5%2FYsULEtOCJebHmupUZaPmBgM%3D

然而,该URL返回403错误。您有任何想法我做错了什么吗?


1
你可以尝试在字符串签名中删除URL编码吗?例如,规范资源应该是/blob/mytest/privatefiles/1/qux.txt - Gaurav Mantri
你好,有更新了吗? - Gary Liu
@GauravMantri 是的,看起来这样做就可以了,不知道我从哪里得到的想法,认为这些字段必须被编码; 你能把它作为答案添加进去,这样我就可以接受它了吗? - adrianp
我现在明白了OP的意思...这里有一个链接,链接到可以实现所需功能的代码 https://github.com/yokawasa/azure-functions-python-samples/blob/master/v1functions/blob-sas-token-generator/function/run.py。我不是作者!! - Jordan Simba
4个回答

11

更新,使用最新的Python存储库,这是我用来生成SAS令牌的方法:

def generate_sas_token(file_name):
    sas = generate_blob_sas(account_name=AZURE_ACC_NAME,
                            account_key=AZURE_PRIMARY_KEY,
                            container_name=AZURE_CONTAINER,
                            blob_name=file_name,
                            permission=BlobSasPermissions(read=True),
                            expiry=datetime.utcnow() + timedelta(hours=2))

    logging.info('https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+file_name+'?'+sas)
    sas_url ='https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+file_name+'?'+sas
    return sas_url

Python 3.6和azure-storage-blob包。


8
生成Python中的SAS令牌最简单的方法是利用Azure Storage SDK for Python。请考虑以下代码片段:
import time
import uuid
import hmac
import base64
import hashlib
import urllib
from datetime import datetime, timedelta
from azure.storage import (
    AccessPolicy,
    ResourceTypes,
    AccountPermissions,
    CloudStorageAccount,
)
from azure.storage.blob import (
    BlockBlobService,
    ContainerPermissions,
    BlobPermissions,
    PublicAccess,
)

AZURE_ACC_NAME = '<account_name>'
AZURE_PRIMARY_KEY = '<account_key>'
AZURE_CONTAINER = '<container_name>'
AZURE_BLOB='<blob_name>'

def generate_sas_with_sdk():
    block_blob_service = BlockBlobService(account_name=AZURE_ACC_NAME, account_key=AZURE_PRIMARY_KEY)    
    sas_url = block_blob_service.generate_blob_shared_access_signature(AZURE_CONTAINER,AZURE_BLOB,BlobPermissions.READ,datetime.utcnow() + timedelta(hours=1))
    #print sas_url
    print 'https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+AZURE_BLOB+'?'+sas_url

generate_sas_with_sdk()

此外,要通过普通的Python脚本生成SAS令牌,您可以参考https://github.com/Azure/azure-storage-python/blob/master/azure/storage/sharedaccesssignature.py#L173上的源代码获取更多提示。

1
查看了他们的代码(更精确的参考是this class),但我无法弄清楚他们在做什么不同的事情;出于客观原因,我希望我的代码能够正常工作,而不使用SDK。 - adrianp
1
损坏的链接... :( - jtlz2
1
@dreftymac 我的想法类似于为什么我会使用库对URL进行编码,而不是重复已经实现和维护在库/模块中的标准。这样可以减少错误、减少维护成本、减少测试范围等等... - Jordan Simba
1
@JordanSimba,没错,这很有道理。你实际上提出了一个不使用SDK的好观点。为什么?因为在某些情况下,与使用基于纯REST API方法相比,SDK实际上可能具有更高的维护要求。当然,这取决于SDK(它是多新的,使用的语言等等),但并不总是SDK方法涉及较少的维护。 - dreftymac
1
这是事实。但SDK存在是为了方便,如果使用任何一个SDK提供的功能,最好全部使用!因此,如果OP正在使用完全基于HTTP的方法,那么我同意他们应该继续保持这种方式,否则就使用已经付费的工具包中的工具。 - Jordan Simba
显示剩余3条评论

5

以下是适用于Python3和最新版本的Azure Storage Blob SDK的更新代码片段:

from datetime import datetime, timedelta
from azure.storage.blob import (
    BlockBlobService,
    ContainerPermissions,
    BlobPermissions,
    PublicAccess,
)

AZURE_ACC_NAME = '<account_name>'
AZURE_PRIMARY_KEY = '<account_key>'
AZURE_CONTAINER = '<container_name>'
AZURE_BLOB='<blob_name>'

block_blob_service = BlockBlobService(account_name=AZURE_ACC_NAME, account_key=AZURE_PRIMARY_KEY)
sas_url = block_blob_service.generate_blob_shared_access_signature(AZURE_CONTAINER,AZURE_BLOB,permission=BlobPermissions.READ,expiry= datetime.utcnow() + timedelta(hours=1))
print('https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+AZURE_BLOB+'?'+sas_url)

2
根据文档(请参见构建签名字符串部分),传递给签名字符串的参数必须进行URL解码。从链接中可以看到:

要构建共享访问签名的签名字符串,首先从组成请求的字段构造字符串,然后将字符串编码为UTF-8并使用HMAC-SHA256算法计算签名。请注意,包括在签名字符串中的字段必须进行URL解码。

请在您的签名字符串中使用未编码的参数值,这样就可以解决问题了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接