Azure.Storage.Blobs.ServiceRestClient.GetUserDelegationKey:System.ObjectDisposedException:无法访问已关闭的流

Posted

技术标签:

【中文标题】Azure.Storage.Blobs.ServiceRestClient.GetUserDelegationKey:System.ObjectDisposedException:无法访问已关闭的流【英文标题】:Azure.Storage.Blobs.ServiceRestClient.GetUserDelegationKey : System.ObjectDisposedException: Cannot access a closed Stream 【发布时间】:2021-12-21 21:29:00 【问题描述】:

我们在使用 GetUserDelegationKey 生成 SAS 令牌时遇到异常,这是我们在并发请求中遇到的异常。

System.ObjectDisposedException: Cannot access a closed Stream.
   at System.IO.MemoryStream.Write(ReadOnlySpan`1 buffer)
   at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
   at System.IO.StreamWriter.Dispose(Boolean disposing)
   at System.Xml.XmlTextWriter.Close()
   at Azure.Core.XmlWriterContent.Dispose()
   at Azure.Core.Pipeline.HttpClientTransport.PipelineRequest.Dispose()
   at Azure.Core.HttpMessage.Dispose()
   at Azure.Storage.Blobs.ServiceRestClient.GetUserDelegationKey(KeyInfo keyInfo, Nullable`1 timeout, CancellationToken cancellationToken)
   at Azure.Storage.Blobs.BlobServiceClient.GetUserDelegationKeyInternal(Nullable`1 startsOn, DateTimeOffset expiresOn, Boolean async, CancellationToken cancellationToken)
   at Azure.Storage.Blobs.BlobServiceClient.GetUserDelegationKey(Nullable`1 startsOn, DateTimeOffset expiresOn, CancellationToken cancellationToken)
   at service.mediastorage.DefsultCredntialStorageService.GetUrlWithAccessToken(String url, Double expiredInHours)
   at service.services.VideoService.Convert(Video video) in /home/vsts/work/Source/service.services/VideoService.cs:line 456
   at System.Linq.Enumerable.SelectListIterator`2.ToList()
   at service.services.VideoService.GetVideosByPaginationaAsync(VideoFilterQueryParams filterParameters, LoggedInUserInfo loggedInUserInfo) in /home/vsts/work/Source/service.services/VideoService.cs:line 175
   at service.api.Controllers.VideoController.GetVideosByPaginationAsync(VideoFilterQueryParams videoFilterQueryParameters) in /home/vsts/work/Source/service.api/Controllers/VideoController.cs:line 216

以下代码用于生成 sastoken

        public  string GetUrlWithAccessToken(string url, double expiredInHours = 0)
        
            if (string.IsNullOrEmpty(url)) return null;
            var uri = new Uri(url);

            var blobClient = new BlobClient(uri, GetDefaultCredentials());
            var blobServiceClient = GetBlobServiceClient(_amsSettings.StorageEndPointUrl);

            UserDelegationKey userDelegationKey =  blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow,
                                                                               DateTimeOffset.UtcNow.AddHours(1));
            string sasTokenUrl = GetBlobUrlWithAccessToken(userDelegationKey, blobClient, url, expiredInHours);
            return sasTokenUrl;
        
        
         private string GetBlobUrlWithAccessToken(UserDelegationKey userDelegationKey, BlobClient blobClient,string url,double expiredInHours)
        
            // Create a SAS token
            BlobSasBuilder sasBuilder = new BlobSasBuilder()
            
                BlobContainerName = blobClient.GetParentBlobContainerClient().Name,
                BlobName = blobClient.Name,
                Resource = "b"
            ;

          
            sasBuilder.ExpiresOn = DateTimeOffset.UtcNow.AddHours(expiredInHours);
            sasBuilder.SetPermissions(BlobSasPermissions.Read);

            // Add the SAS token to the container URI.
            BlobUriBuilder blobUriBuilder = new BlobUriBuilder(blobClient.Uri)
            
                // Specify the user delegation key.
                Sas = sasBuilder.ToSasQueryParameters(userDelegationKey,
                                                      blobClient.AccountName)
            ;
            var sasToken = blobUriBuilder.Sas.ToString();
            return url + "?" + sasToken;
        

谁能帮我解决这个问题。

编辑:

我们正在使用托管标识,当我们进行负载测试时,一些请求失败,但其中一些成功。

谢谢

【问题讨论】:

【参考方案1】:

您能否尝试使用此代码生成 SAS 令牌

private static string GetSharedAccessSignature(
           string accountName,
           string accountkey,
           string blobContainer,
           string blobName,
           DateTimeOffset sharedAccessStartTime,
           DateTimeOffset sharedAccessExpiryTime)
    
        var canonicalNameFormat = $"/blob/accountName/blobContainer/blobName";
        var st = sharedAccessStartTime.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
        var se = sharedAccessExpiryTime.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
        var sasVersion = "2016-05-31";
    
        string stringToSign = string.Format("0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12", new object[]
        
            "r",
            st,
            se,
            canonicalNameFormat,
            string.Empty,
            string.Empty,
            string.Empty,
            sasVersion,
            string.Empty,
            string.Empty,
            string.Empty,
            string.Empty,
            string.Empty
        );
    
        var sas = GetHash(stringToSign, accountkey);
    
        var credentials =
            $"?sv=sasVersion&sr=b&sig=UrlEncoder.Default.Encode(sas)&st=UrlEncoder.Default.Encode(st)&se=UrlEncoder.Default.Encode(se)&sp=r";
    
        string blobUri = $"https://accountName.blob.core.windows.net/blobContainer/blobName";
        return blobUri + credentials;
    
    
    private static string GetHash(string stringToSign, string key)
    
        byte[] keyValue = Convert.FromBase64String(key);
    
        using (HMACSHA256 hmac = new HMACSHA256(keyValue))
        
            return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        
    

更多详情请参考SO Thread:

【讨论】:

OP 想要创建一个User Delegation SAS Token。您在回答中提到的代码创建了一个Service SAS Token 是的,想要创建用户委托 SAS 令牌,因为我们使用的是 ManagedIdentity 和最新的 sdk 版本 Azure.Storage.Blobs=12.9.1 请参考此文档:codewithadam.com/Azure/… 它和我的代码@ShrutiJoshi-MT 一样

以上是关于Azure.Storage.Blobs.ServiceRestClient.GetUserDelegationKey:System.ObjectDisposedException:无法访问已关闭的流的主要内容,如果未能解决你的问题,请参考以下文章