使用JS生成Azure Blob存储SA令牌

时间:2019-01-30 19:24:03

标签: javascript azure azure-storage

我在azure函数中运行此函数,以获取用于浏览器应用程序的sas令牌,以将其上传到azure blob存储:

var azure = require('azure-storage');

module.exports = function(context, req) {
  if (req.body.container) {
    // The following values can be used for permissions:
    // "a" (Add), "r" (Read), "w" (Write), "d" (Delete), "l" (List)
    // Concatenate multiple permissions, such as "rwa" = Read, Write, Add
    context.res = generateSasToken(
      context,
      req.body.container,
      req.body.blobName,
      req.body.permissions
    );
  } else {
    context.res = {
      status: 400,
      body: "Specify a value for 'container'"
    };
  }

  context.done(null, context);
};

function generateSasToken(context, container, blobName, permissions) {
  var connString = process.env.AzureWebJobsStorage;
  var blobService = azure.createBlobService(connString);

  // Create a SAS token that expires in an hour
  // Set start time to five minutes ago to avoid clock skew.
  var startDate = new Date();
  startDate.setMinutes(startDate.getMinutes() - 5);
  var expiryDate = new Date(startDate);
  expiryDate.setMinutes(startDate.getMinutes() + 60);

  permissions = azure.BlobUtilities.SharedAccessPermissions.READ +
                azure.BlobUtilities.SharedAccessPermissions.WRITE +
                azure.BlobUtilities.SharedAccessPermissions.DELETE +
                azure.BlobUtilities.SharedAccessPermissions.LIST;

  var sharedAccessPolicy = {
    AccessPolicy: {
      Permissions: permissions,
      Start: startDate,
      Expiry: expiryDate
    }
  };

  var sasToken = blobService.generateSharedAccessSignature(
    container,
    blobName,
    sharedAccessPolicy
  );

  context.log(sasToken);

  return {
    token: sasToken,
    uri: blobService.getUrl(container, blobName, sasToken, true)
  };
}

然后我在客户端调用此网址,然后尝试使用以下代码上传:

const search = new URLSearchParams(`?${token}`);

const sig = encodeURIComponent(search.get('sig'));

const qs = `?sv=${search.get('sv')}&ss=b&srt=sco&sp=rwdlac&se=${search.get('sv')}&st=${search.get(
  'st'
)}&spr=https&sig=${sig}`;

return `${url}/${containerName}/${filename}${qs}`;

哪个生成这样的网址:

https://mystorage.blob.core.windows.net/mycontainer/latest.png?sv=2018-03-28&ss=b&srt=sco&sp=rwdlac&se=2018-03-28&st=2019-01-30T19:11:10Z&spr=https&sig=g0sceq3EkiAQTvyaZ07C+C4SZQz9FaGTV4Zwq4HkAnc=

哪个返回此错误:

  

403(服务器无法验证请求。请确保   授权标头的格式正确,包括签名。)

如果我从azure门户网站生成了sas令牌,则它可以正常工作,因此生成的url如下所示:

  

https://mystorage.blob.core.windows.net/mycontainer/latest.png?sv=2018-03-28&ss=b&srt=sco&sp=rwdlac&se=2019-01-31T03:01:43Z&st=2019-01-30T19:01:43Z&spr=https&sig=ayE4gt%2FDfDzjv5DjMaD7AS%2F176Bi4Q6DWJNlnDzl%2FGc%3D

但是我的网址看起来像这样:

  

https://mystorage.blob.core.windows.net/mycontainer/latest.png?sv=2018-03-28&ss=b&srt=sco&sp=rwdlac&se=2019-01-31T03:34:21Z&st=2019-01-30T19:34:21Z&spr=https&sig=Dx8Vm4XPnD1rn9uyzIAXZEfcdbWb0HjmOq%2BIq42Q%2FOM%3D

我不知道该怎么做

3 个答案:

答案 0 :(得分:0)

您的Azure功能代码正确,并且

var sasToken = blobService.generateSharedAccessSignature(
    container,
    blobName,
    sharedAccessPolicy
);

正是您上传blob所需的sasToken。无需像第二个代码片段中那样再次处理令牌(实际上是错误处理)。

预计Azure门户(Account SAS)中的sas令牌与代码(Service SAS)中生成的sas令牌不同。看看doc

总结

  1. 确保连接字符串属于您要连接的存储。您可以避免麻烦,直接将var connString = process.env.AzureWebJobsStorage;替换为var connString = "connectionStringGotFromPortal";

  2. 如果确认为1,则您的Azure函数代码正确,并按预期返回令牌

    {
      token: sasToken,
      uri: blobService.getUrl(container, blobName, sasToken, true)
    };  
    
  3. 根据您提供的第二个代码段,您只需要

    return `${url}/${containerName}/${filename}?${token}`; 
    

    如果令牌与返回的函数相同。

答案 1 :(得分:0)

问题在于,在服务器端代码中,您正在创建Service SAS,然后仅获取代码的签名部分(sig),并在客户端上创建Account SAS

由于用于创建令牌的参数现已更改(在原始参数中,您没有诸如sssrt等的参数,但是在创建自己的URL时,插入这些参数),那么当您使用修改后的SAS URL时,会出现403错误。发生这种情况是因为服务器再次基于URL参数计算签名,并将其与URL中传递的签名进行比较。由于两个签名不匹配,因此您将收到403错误。

由于您要返回Blob的SAS URL,因此无需在客户端上创建URL。您可以简单地使用从客户端API层返回的uri并使用该文件进行上传。

答案 2 :(得分:0)

正如Jerry Liu的答案所解释的那样,您的Azure函数会生成正确的令牌,并且已经为您提供了正确的uri使用,其中包括您的blob名称和令牌。

在客户端,您也可以使用azure-sdk-for-js

// This is the response from your api with token and uri    
const uri = response.uri;

const pipeline = StorageURL.newPipeline(new AnonymousCredential());

// Your uri already includes the full blob url with SAS signature
const blockBlobURL = BlockBlobURL.fromBlobURL(new BlobURL(uri, pipeline));
const uploadBlobResponse = await blockBlobURL.upload(
  Aborter.none,
  file,
  file.size,
  { blobHTTPHeaders: { blobContentType: `${mime}; charset=utf-8`} }
);