Azure 存储访问策略,签名不匹配

Azure Stored access policy, Signature did not match

我在尝试使用从策略生成的 Sas 访问 blob(图像)时收到以下错误。 MS 学习项目的一部分:https://docs.microsoft.com/en-us/learn/modules/control-access-to-azure-storage-with-sas/6-exercise-use-stored-access-policies

运行练习了两次,还是一样的结果。知道为什么签名是个问题吗?

https://medicalrecords20184.blob.core.windows.net/patient-images/patient-116139-nq8z7f.jpg?sv=2020-08-04&si=patient-images-policy&sr=c&sig=xxxxxxx

AuthenticationFailed 服务器无法验证请求。确保 Authorization header 的值格式正确,包括签名。 RequestId:328fa4c1-401e-004c-49d2-06e232000000 Time:2022-01-11T10:02:52.3736100Z

签名不匹配。使用的签名字符串是 /blob/medicalrecords10150/patient-images patient-images-policy 2020-08-04 c

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration; 

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Sas;
using Azure.Storage;

namespace patientrecords.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class PatientRecordsController : ControllerBase
    {
        private readonly ILogger<PatientRecordsController> _logger;
        private IConfiguration _iconfiguration;

        private BlobContainerClient _container;

        private String _storedPolicyID = "patient-images-policy";
 

       public PatientRecordsController(ILogger<PatientRecordsController> logger, IConfiguration iconfiguration)
        {
            _logger = logger;
            _iconfiguration = iconfiguration; 
            _container = new BlobContainerClient(
                _iconfiguration.GetValue<string>("StorageAccount:ConnectionString"),
                _iconfiguration.GetValue<string>("StorageAccount:Container")
            );
            CreateStoredAccessPolicy();
        }

        // GET PatientRecord
        [HttpGet]
        public IEnumerable<PatientRecord> Get()
        {
            List<PatientRecord> records = new List<PatientRecord>();

            foreach (BlobItem blobItem in _container.GetBlobs())
            {
                BlobClient blob = _container.GetBlobClient(blobItem.Name);
                var patient = new PatientRecord { name=blob.Name, imageURI=blob.Uri.ToString() };
                records.Add(patient);
            }

            return records;
        }

        // GET PatientRecord/patient-nnnnnn
        [HttpGet("{Name}")]
        public PatientRecord Get(string name)
        {
            BlobClient blob = _container.GetBlobClient(name);
            return new PatientRecord { name=blob.Name, imageURI=blob.Uri.AbsoluteUri };
        }


         // GET PatientRecord/patient-nnnnnn/secure
        [HttpGet("{Name}/{secure}")]
        public PatientRecord Get(string name, string flag)
        {
            BlobClient blob = _container.GetBlobClient(name);
            return new PatientRecord { name=blob.Name, imageURI=blob.Uri.AbsoluteUri, sasToken=GetBlobSas() };
        }
        // Build a SAS token for the given blob
        private string GetBlobSas(BlobClient blob)
        {
            // Create a user SAS that only allows reading for a minute
            BlobSasBuilder sas = new BlobSasBuilder 
            {
                BlobContainerName = blob.BlobContainerName,
                BlobName = blob.Name,
                Resource = "b",
                ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(1)
            };
            // Allow read access
            sas.SetPermissions(BlobSasPermissions.Read);

            // Use the shared key to access the blob
            var storageSharedKeyCredential = new StorageSharedKeyCredential(
                _iconfiguration.GetValue<string>("StorageAccount:AccountName"),
                _iconfiguration.GetValue<string>("StorageAccount:AccountKey")
            );

            return '?' + sas.ToSasQueryParameters(storageSharedKeyCredential).ToString();
        }

        // Use a stored access policy for the SAS
        private void CreateStoredAccessPolicy()
        {
            // Create a stored access policy for our blobs
            BlobSignedIdentifier identifier = new BlobSignedIdentifier
            {
                Id = _storedPolicyID,
                AccessPolicy = new BlobAccessPolicy
                {
                    //ExpiresOn = DateTimeOffset.UtcNow.AddHours(1),
                    Permissions = "r"
                }
            };

            _container.SetAccessPolicy(permissions: new BlobSignedIdentifier[] { identifier });
        }


        // Build a SAS token for the given blob
        private string GetBlobSas()
        {
            // Create a user SAS that only allows reading for a minute
            BlobSasBuilder sas = new BlobSasBuilder 
            {
                Identifier = _storedPolicyID
            };

            // Use the shared key to access the blob
            var storageSharedKeyCredential = new StorageSharedKeyCredential(
                _iconfiguration.GetValue<string>("StorageAccount:AccountName"),
                _iconfiguration.GetValue<string>("StorageAccount:AccountKey")
            );

            return '?' + sas.ToSasQueryParameters(storageSharedKeyCredential).ToString();
        }

    }



}

我认为您收到此错误是因为您没有在 GetBlobSas() 方法中指定 blob 容器名称。由于省略了 blob 容器名称,因此会为 $root blob 容器计算 SAS 令牌。由于 SAS 令牌是为 $root blob 容器计算的,并且您正在将它与另一个 blob 容器一起使用,因此您会收到此授权失败错误。

我注意到的另一个问题是您没有在 SAS 令牌中包含到期时间。它不在您的访问策略中,也不在您使用访问策略获取 SAS 令牌时。

请尝试使用以下代码:

// Build a SAS token for the given blob
 private string GetBlobSas()
 {
     // Create a user SAS that only allows reading for a minute
     BlobSasBuilder sas = new BlobSasBuilder 
     {
         Identifier = _storedPolicyID,
         ExpiresOn = DateTimeOffset.UtcNow.AddHours(1),
         BlobContainerName = _iconfiguration.GetValue<string>("StorageAccount:Container")
     };

     // Use the shared key to access the blob
     var storageSharedKeyCredential = new StorageSharedKeyCredential(
         _iconfiguration.GetValue<string>("StorageAccount:AccountName"),
         _iconfiguration.GetValue<string>("StorageAccount:AccountKey")
     );

     return '?' + sas.ToSasQueryParameters(storageSharedKeyCredential).ToString();
 }

您将从上面获得的 SAS 令牌将在创建后 1 小时后过期。