在Java中生成SAS令牌,以将文件下载到Azure数据存储容器中

时间:2018-12-06 14:22:27

标签: java rest azure azure-storage

尝试生成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%2BoaIeoBh1wjaO1p4xVFx4nRZz%2Fz >

似乎我需要这样的字符串:

            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编码的开始/有效日期,以防万一。我想念什么?

2 个答案:

答案 0 :(得分:2)

要解决的三点问题

    @Gaurav提到的
  1. getHMAC256方法问题

  2. 不对expiry中的startstringToSign进行编码,否则签名不匹配。因为url中的编码部分将由Azure存储服务解码以计算期望的签名。

  3. stringToSign中,在\n之后错过了一个azureApiVersion

这是完整的样本。

 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 :(得分:0)

我有一个更简单的方法

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);