如何在 Java 中基于 SAS 令牌访问 Azure WASB 容器?

Posted

技术标签:

【中文标题】如何在 Java 中基于 SAS 令牌访问 Azure WASB 容器?【英文标题】:How to Access Azure WASB Container Based on SAS Token in Java? 【发布时间】:2019-06-23 22:37:46 【问题描述】:

我正在尝试使用 SAS 密钥以 Java 代码从 Azure WASB 检索容器。这是在 HDFS 代码中完成的,但无论出于何种原因,我似乎都无法让它工作。我设法将其简化为下面的应用程序,这也不起作用。我认为这要么是我们如何生成 SAS 令牌的问题,要么是 Azure 帐户的权限问题。有人可以看看这个并指出我可能出现的问题的正确方向吗?谢谢!

public static void main(String[] arguments)

    try 
        String storage_account = "wasbvalidation";
        String container = "demoengagement1";
        CloudBlobClient blobClient = getBlobClient(storage_account);

        CloudBlobContainer blobContainer = blobClient.getContainerReference(container);

        blobContainer.downloadAttributes(); // This call succeeds

        SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy();
        policy.setPermissions(EnumSet.allOf(SharedAccessBlobPermissions.class));
        policy.setSharedAccessStartTime(Date.valueOf(LocalDate.now().minusYears(2)));
        policy.setSharedAccessExpiryTime(Date.valueOf(LocalDate.now().plusYears(2)));

        String sas = blobContainer.getUri().toString() + "?" + blobContainer.generateSharedAccessSignature(policy, null, null, SharedAccessProtocols.HTTPS_ONLY);

        // Code after this point is emulating what HDFS is doing, so I'd rather not change it.
        URI blobUri = new URI(blobContainer.getUri().toString());
        StorageCredentials credentials = new StorageCredentialsSharedAccessSignature(sas);
        CloudBlobContainer sasContainer = new CloudBlobContainer(blobUri, credentials);
        sasContainer.downloadAttributes(); // This call fails, however.
     catch (Exception e) 
        e.printStackTrace();
    


private static CloudBlobClient getBlobClient(String storageAccount) throws NullPointerException 
    String storageConnectionString = "DefaultEndpointsProtocol=https;" + "AccountName=" + storageAccount + ";" + "AccountKey=" + accountKey;
    CloudStorageAccount csa = null;
    try 
        csa = CloudStorageAccount.parse(storageConnectionString);
     catch (Exception ex) 
        ex.printStackTrace();
    
    CloudBlobClient blobClient = csa.createCloudBlobClient();
    return blobClient;

【问题讨论】:

【参考方案1】:

根据您的代码,我认为您希望通过使用 SAS 为容器构建 url 来获取 blob 容器的属性和元数据。但是,SharedAccessBlobPolicy 生成的 SAS 字符串就像 sig=1G7tiQnLEtbjk2RSNuUSKH7gLNVZjqhuLQL%2Fci%2FXS50%3D&st=2017-01-30T16%3A00%3A00Z&se=2021-01-30T16%3A00%3A00Z&sv=2018-03-28&sp=racwdl&sr=b 用于 blob(sr=b),而不用于容器(sr=c,例如从 Azure 存储资源管理器复制的 st=2019-01-31T08%3A38%3A46Z&se=2019-02-01T08%3A38%3A46Z&sp=rl&sv=2018-03-28&sr=c&sig=KnynNYBUtzNSYtBEcYakMrhAXPRIk60wztB3BFv5b%2Bs%3D)。

我尝试通过下面的代码使用CloudStorageAccountSharedAccessAccountPolicy 为blob 生成Account SAS,但它仍然不起作用。

账户 SAS。账户 SAS 授权访问一个或多个存储服务中的资源。通过服务 SAS 可用的所有操作也可通过帐户 SAS 进行。此外,通过帐户 SAS,您可以委派对适用于给定服务的操作的访问权限,例如获取/设置服务属性和获取服务统计信息。您还可以委派对服务 SAS 不允许的 blob 容器、表、队列和文件共享的读取、写入和删除操作的访问权限。有关构建账户 SAS 令牌的详细信息,请参阅构建账户 SAS。

SharedAccessAccountPolicy accountPolicy = new SharedAccessAccountPolicy();
accountPolicy.setPermissions(EnumSet.allOf(SharedAccessAccountPermissions.class));
accountPolicy.setSharedAccessStartTime(Date.valueOf(LocalDate.now().minusYears(2)));
accountPolicy.setSharedAccessExpiryTime(Date.valueOf(LocalDate.now().plusYears(2)));

String sas = csa.generateSharedAccessSignature(accountPolicy);

我测试下面的代码,

StorageCredentials credentials = new StorageCredentialsSharedAccessSignature(sas);
CloudBlobContainer sasContainer = new CloudBlobContainer(new URI(container2.getUri().toString()+"?"+sas), credentials);
sasContainer.downloadAttributes();

然后获取异常。

Exception in thread "main" java.lang.IllegalArgumentException: Cannot provide credentials as part of the address and as constructor parameter. Either pass in the address or use a different constructor.

或者测试代码CloudBlobContainer sasContainer = new CloudBlobContainer(new URI(container2.getUri().toString()+"?"+sas))获取异常。

Exception in thread "main" com.microsoft.azure.storage.StorageException: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.

我研究了SDK源代码后,似乎是由Azure Java Storage SDK v8.0.0的实现引起的。也许你可以向微软报告这个问题。

我尝试通过下面的代码使用 Azure Java Storage SDK v10 使用 SAS 生成容器 url,它工作正常。

v10 的 Maven 依赖项:

<!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-storage-blob -->
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-storage-blob</artifactId>
    <version>10.4.0</version>
</dependency>

使用 SAS 生成容器 url 的代码:

String accountName = "<your account name>";
String accountKey = "<your account key>";
SharedKeyCredentials credentials = new SharedKeyCredentials(accountName, accountKey);
final ServiceURL serviceURL = new ServiceURL(new URL("http://" + accountName + ".blob.core.windows.net"), StorageURL.createPipeline(credentials, new PipelineOptions()));
String containerName = "<container name>";
ServiceSASSignatureValues values = new ServiceSASSignatureValues()
                .withProtocol(SASProtocol.HTTPS_ONLY) // Users MUST use HTTPS (not HTTP).
                .withExpiryTime(OffsetDateTime.now().plusDays(2)) // 2 days before expiration.
                .withContainerName(containerName)
                .withBlobName(blobName);
ContainerSASPermission permission = new ContainerSASPermission()
                .withRead(true)
                .withAdd(true)
                .withWrite(true);
values.withPermissions(permission.toString());
SASQueryParameters serviceParams = values.generateSASQueryParameters(credentials);
String sas = serviceParams.encode();

String containerUrlWithSAS = String.format(Locale.ROOT, "https://%s.blob.core.windows.net/%s%s",
                accountName, containerName, sas);
HttpPipeline pipeline = new HttpPipelineBuilder().build();
ContainerURL sasContainer = new ContainerURL(new URL(containerUrlWithSAS), pipeline);
sasContainer.getProperties();

注意:SDK v10ContainerURLgetProperties 函数与SDK v8CloudBlobContainerdownloadAttributes 类似,同样返回容器的元数据和系统属性。

【讨论】:

嗯,有几件事:1. 我们一直在使用 azure-storage,而不是 azure-storage-blob。我已经尝试过最新版本(8.0.0)、7.0.0 和 Hadoop 使用的版本(5.4.0)。所有这些都有相同的结果。 2. 我们用上面的代码返回的 SAS 令牌实际上有 sr=c,所以它正确地给了我们一个容器 SAS 密钥。 3.我尝试使用您上面的确切代码,它仍然给我同样的错误。 v10 的 getProperties 中可能已修复此问题,但由于他们使用的是 v5.4,因此此处无法使用。有进一步的指导吗? 总结这个答案,最后的答案是在 v10 之前,不支持使用 SAS 令牌作为容器的 downloadAttributes,例如我正在尝试做的事情。

以上是关于如何在 Java 中基于 SAS 令牌访问 Azure WASB 容器?的主要内容,如果未能解决你的问题,请参考以下文章

Azure 事件中心 python 中 SAS 令牌的连接字符串

使用 SAS 令牌从 Azure 阶段读取时出现雪花错误

完成文件上传后,如何使用 nodejs 撤销 Azure 的共享访问签名(SAS)?

第9章 用Java,Lua,Python和REST API开发基于SAS Viya的应用:SAS Viya开放平台介绍

如何在 C# 中使用服务主体(clientId 和 clientSecret)为 Azure Data Lake Store(Gen-2)创建 SAS 令牌?

要上传的特定文件的 Azure SAS 令牌?具有读取和到期时间(JAVA)