在 Java 中生成 SAS 令牌以下载 Azure 数据存储容器中的文件
Posted
技术标签:
【中文标题】在 Java 中生成 SAS 令牌以下载 Azure 数据存储容器中的文件【英文标题】:Generating an SAS Token in Java to download a file in an Azure Data Storage container 【发布时间】:2019-05-08 06:32:00 【问题描述】:尝试生成 SAS 令牌以访问存储帐户中的某些文件。我正在使用此处列出的方法:
https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
现在,我遇到的问题是,我一辈子都无法让 sasToken 字符串工作。如果我通过门户(存储帐户中的共享访问签名)生成令牌,我可以使用提供的令牌通过 URL 访问这些文件。
但是,我还不能使用上面链接的方法通过 Java 以编程方式生成 SAS 令牌。我认为我的问题是正在加密的 StringToSign。在构造要加密的字符串时,我一直在关注这个例子:
https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-an-account-sas
我所有的努力都导致了:
<AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail>
或
<AuthenticationErrorDetail>Signature did not match. String to sign used was <insert string details here>
查看 Portal 生成的适合我的 sasToken:
?sv=2017-11-09&ss=f&srt=o&sp=r&se=2018-12-06T22:15:20Z&st=2018-12-06T14:15:20Z&spr=https&sig=%2Bi1TWv5D80U%2BoaIeoBh1wjaO1p4xVFYz%3nRZt%2Fzwi p>
看来我需要这样的字符串:
String stringToSign = accountName + "\n" +
"r\n" +
"f\n" +
"o\n" +
URLEncoder.encode(start, "UTF-8") + "\n" +
URLEncoder.encode(expiry, "UTF-8") + "\n" +
"\n" +
"https\n" +
azureApiVersion;
其中 accountName 是 Azure 的存储帐户名称,start/expiry 是开始和到期字符串(即 2018-12-06T22:15:20Z),azureApiVersion 是“2017-11-09”。
然后我尝试在构造字符串后返回令牌,如下所示:
String signature = getHMAC256(key, stringToSign);
sasToken = "sv=" + azureApiVersion +
"&ss=f" +
"&srt=o" +
"&sp=r" +
"&se=" +URLEncoder.encode(expiry, "UTF-8") +
"&st=" + URLEncoder.encode(start, "UTF-8") +
"&spr=https" +
"&sig=" + URLEncoder.encode(signature, "UTF-8");
我也尝试了 URL 编码,而不是 URL 编码开始/到期日期,以防万一搞砸了。我错过了什么?
【问题讨论】:
您使用的密钥的值是多少?是你从portal得到的字符串还是base64解码账户密钥字符串得到的字节数组? 我的密钥的值是 Azure 中存储帐户上的访问密钥之一。 我相信问题出在您的getHMAC256
方法中。我查找了它的代码,它只是从字符串值中获取字节。实际上,您需要对密钥字符串进行 base64 解码并在此方法中使用该字节数组。
好的,我尝试更改基本 getHMAC256 方法以使用: SecretKeySpec secret_key = new SecretKeySpec(Base64.getDecoder().decode(key), "HmacSHA256");不幸的是,收到了“签名不匹配。使用的签名字符串是 三点修复
@Gaurav 提到的getHMAC256
方法问题
不要在stringToSign
中编码expiry
和start
,否则签名将不匹配。因为 url 中的编码部分会被 Azure Storage Service 解码,计算出预期的签名。
在stringToSign
中,在azureApiVersion
之后错过一个\n
。
这是完整的示例。
public static void GetFileSAS()
String accountName = "accountName";
String key = "accountKey";
String resourceUrl = "https://"+accountName+".file.core.windows.net/fileShare/fileName";
String start = "startTime";
String expiry = "expiry";
String azureApiVersion = "2017-11-09";
String stringToSign = accountName + "\n" +
"r\n" +
"f\n" +
"o\n" +
start + "\n" +
expiry + "\n" +
"\n" +
"https\n" +
azureApiVersion+"\n";
String signature = getHMAC256(key, stringToSign);
try
String sasToken = "sv=" + azureApiVersion +
"&ss=f" +
"&srt=o" +
"&sp=r" +
"&se=" +URLEncoder.encode(expiry, "UTF-8") +
"&st=" + URLEncoder.encode(start, "UTF-8") +
"&spr=https" +
"&sig=" + URLEncoder.encode(signature, "UTF-8");
System.out.println(resourceUrl+"?"+sasToken);
catch (UnsupportedEncodingException e)
e.printStackTrace();
private static String getHMAC256(String accountKey, String signStr)
String signature = null;
try
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(accountKey), "HmacSHA256");
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
sha256HMAC.init(secretKey);
signature = Base64.getEncoder().encodeToString(sha256HMAC.doFinal(signStr.getBytes("UTF-8")));
catch (Exception e)
e.printStackTrace();
return signature;
【讨论】:
噢!你对第 1 点和第 3 点是绝对正确的(我正在尝试使用编码和未编码的日期,只是碰巧在我的示例中粘贴了编码版本)谢谢! 除上述之外 - 我发现本节在描述stringToSign
: docs.microsoft.com/en-us/rest/api/storageservices/… 的内容时很有用
我遇到了下载链接不会过期的问题。原来,我的浏览器正在缓存请求的结果。在另一个浏览器中尝试该链接证明它确实已过期。看到这个:social.msdn.microsoft.com/Forums/en-US/…。只是把它放在那里......【参考方案2】:
我有一个更简单的方法
SharedAccessAccountPolicy sharedAccessAccountPolicy = new SharedAccessAccountPolicy();
sharedAccessAccountPolicy.setPermissionsFromString("racwdlup");
long date = new Date().getTime();
long expiryDate = new Date(date + 8640000).getTime();
sharedAccessAccountPolicy.setSharedAccessStartTime(new Date(date));
sharedAccessAccountPolicy.setSharedAccessExpiryTime(new Date(expiryDate));
sharedAccessAccountPolicy.setResourceTypeFromString("sco");
sharedAccessAccountPolicy.setServiceFromString("bfqt");
String sasToken = "?" + storageAccount.generateSharedAccessSignature(sharedAccessAccountPolicy);
您可以像这样获取存储帐户:
private String storageConnectionString = "DefaultEndpointsProtocol=https;AccountName=<storage name>;AccountKey=<your key>;EndpointSuffix=core.windows.net";
storageAccount = CloudStorageAccount.parse(storageConnectionString);
【讨论】:
嘿,我在哪里可以获得 mvn 中的 SharedAccessAccountPolicy 库? @Rodolfo Velasco 我想是这个。 mvnrepository.com/artifact/com.microsoft.azure/azure-storage 如果这不起作用,请告诉我,我会检查。 好吧,我还是检查了。这里是:编译组:'com.microsoft.azure',名称:'azure-storage',版本:'8.0.0' 我在 maven 上使用 v8.0.0 我看到他们有更新的版本。 此时我使用的是最新版本。我需要设置权限才能读取(下载),所以我将“racwdlup”更改为“r”。除此之外,您认为是否需要更改 setResourceTypeFromString("sco") 或 setServiceFromString("bfqt")? 中的任何内容?提前致谢。 @Rodolfo Velasco 老实说,我已经不记得这个权限在 Azure 上是如何工作的了,但他们的文档中有解释。以上是关于在 Java 中生成 SAS 令牌以下载 Azure 数据存储容器中的文件的主要内容,如果未能解决你的问题,请参考以下文章
在 Java 中生成 JWT 令牌以进行 hasura 授权
无法使用 Microsoft 示例在 Azure APIM 中生成 SAS
如何在 Python 中生成 Azure blob SAS URL?