Azure Blob存储错误显示并确保正确形成了授权标头的值,包括签名。

发布于 2025-02-13 04:37:07 字数 6533 浏览 1 评论 0原文

我正在使用RESTAPI使用访问密钥从Azure Blob存储中获取数据。 我有一个邮政集合,可以使用访问密钥来生成Azure的签名字符串。

但是,当我以Java语言实现相同的逻辑时,它会失败。 它给我带来了错误“ 403服务器未能对请求进行身份验证。确保正确形成了授权标头的值,包括签名。:[AuthenticationFailed服务器无法对请求进行身份验证。确保正确形成授权标头的值...(435个字节)]“

这是生成签名字符串的Postman测试PER REQ部分。

    // Should be UTC GMT string
    pm.environment.set("utcStr", new Date().toUTCString());

    // Get hash of all header-name:value
    const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });

    // Construct Signature value for Authorization header
    var signatureParts = [
      pm.request.method.toUpperCase(),
      headers["content-encoding"] || "",
      headers["content-language"] || "",
      headers["content-length"]  || "",
      //    pm.request.body ? pm.request.body.toString().length || "" : "",
      headers["content-md5"] || "",
      headers["content-type"] || "",
      headers["x-ms-date"] ? "" : (pm.environment.get("utcStr") || ""),
      headers["if-modified-since"] || "",
      headers["if-match"] || "",
      headers["if-none-match"] || "",
      headers["if-unmodified-since"] || "",
      headers["range"] || ""
    ];

    // Construct CanonicalizedHeaders
    const canonicalHeaderNames = [];
    Object.keys(headers).forEach(key => {
      if (key.startsWith("x-ms-")) {
          canonicalHeaderNames.push(key);
      }
    });
    // Sort headers lexographically by name
    canonicalHeaderNames.sort();

    const canonicalHeaderParts = [];
    canonicalHeaderNames.forEach(key => {
      let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];

      // Populate variables
      value = pm.environment.replaceIn(value);

      // Replace whitespace in value but not if its within quotes
      if (!value.startsWith("\"")) {
        value = value.replace(/\s+/, " ");
      }

      canonicalHeaderParts.push(`${key}:${value}`);
    });

    // Add headers to signature
    signatureParts.push.apply(signatureParts, canonicalHeaderParts);

    // Construct CanonicalizedResource
    const canonicalResourceParts = [
      `/${pm.environment.get("storeAcc")}${pm.request.url.getPath()}`
    ];
    const canonicalQueryNames = [];
    pm.request.url.query.each(query => {
      canonicalQueryNames.push(query.key.toLowerCase());
    });
    canonicalQueryNames.sort();
    canonicalQueryNames.forEach(queryName => {
      const value = pm.request.url.query.get(queryName);

      // NOTE: This does not properly explode multiple same query params' values
      // and turn them into comma-separated list
      canonicalResourceParts.push(`${queryName}:${value}`);
    });
    // Add resource to signature
    signatureParts.push.apply(signatureParts, canonicalResourceParts);

    console.log("Signature Parts", signatureParts);
 
    // Now, construct signature raw string
    const signatureRaw = signatureParts.join("\n");

    console.log("Signature String", JSON.stringify(signatureRaw));

    // Hash it using HMAC-SHA256 and then encode using base64
    const storageKey = pm.variables.get("accountKey");
    const signatureBytes = CryptoJS.HmacSHA256(signatureRaw,    CryptoJS.enc.Base64.parse(storageKey));
    console.log("signatureBytes",signatureBytes);
    const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);

    console.log("signatureEncoded",signatureEncoded);
    console.log("Storage Account", pm.environment.get("storeAcc"));
    console.log("Storage Key", storageKey);

    // Finally, make it available for headers
    pm.environment.set("sigStr",
       `SharedKey ${pm.environment.get("storeAcc")}:${signatureEncoded}`); 

该代码有效,我生成的签名已进行身份验证,我可以访问BLOB API。

但是我在Java中实现了相同的代码,每次给我一个相同的问题“ 403身份验证错误”。

java代码

    public String generateOauthHeader(String method, UserDetails userFields, String baseUrl, String azureFileListUrl)  {

        String key = userFields.getAccountKey();

        DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ");
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        String startDate = dateFormat.format(new Date());
        
        String stringToSign = method + "\\n\\n\\n\\n\\napplication/json\\n\\n\\n\\n\\n\\n\\nx-ms-date:" + "Tue, 05 Jul 2022 11:45:00 "
                + "GMT\\nx-ms-version:2020-04-08\\n/" + userFields.getAccountName() + "/"
                + userFields.getContainerName();

        if (null != azureFileListUrl) {
            azureFileListUrl =azureFileListUrl.replace("?", "");
            azureFileListUrl =azureFileListUrl.replace("=", ":");
            String[] paramlist = azureFileListUrl.split("&");

            stringToSign = stringToSign + "\\n" + paramlist[1] + "\\n" + paramlist[0];
        }
        stringToSign="\""+stringToSign+"\"";
        System.err.println("Signature string  =  " + stringToSign);
        String signature = getHMAC256(key, stringToSign);

        signature = "SharedKey " + userFields.getAccountName() + ":" + signature;
        System.err.println("Final Signature string  =  " + signature);
        return signature;
    }

    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(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signature;
    }

我在下面添加了大约同时生成的两个字符串。

签名前的样品签名是相同的。我也打印并检查了它们。

postman= "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:45:00 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"

code = "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:44:56 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"

编辑: 这是带有请求的标题。

      requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");
      requestHeaders.put("x-ms-version", "2020-04-08");
      requestHeaders.put("Content-Type", "application/json");
      requestHeaders.put("Authorization", "SharedKey accountName:yvfdsfdssdfF0lGGtEWGU+b7BqFY0UHMkvI=
");

I am using restapi to fetch data from azure blob storage using the access keys.
I have a postman collection which i can use to generate the signature string for the azure using the access keys.

But when i implement the same logic in java language it fails.
It is giving me the error "403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.: [AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly inclu... (435 bytes)]"

here is the postman test pre req part which generate the signature string.

    // Should be UTC GMT string
    pm.environment.set("utcStr", new Date().toUTCString());

    // Get hash of all header-name:value
    const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });

    // Construct Signature value for Authorization header
    var signatureParts = [
      pm.request.method.toUpperCase(),
      headers["content-encoding"] || "",
      headers["content-language"] || "",
      headers["content-length"]  || "",
      //    pm.request.body ? pm.request.body.toString().length || "" : "",
      headers["content-md5"] || "",
      headers["content-type"] || "",
      headers["x-ms-date"] ? "" : (pm.environment.get("utcStr") || ""),
      headers["if-modified-since"] || "",
      headers["if-match"] || "",
      headers["if-none-match"] || "",
      headers["if-unmodified-since"] || "",
      headers["range"] || ""
    ];

    // Construct CanonicalizedHeaders
    const canonicalHeaderNames = [];
    Object.keys(headers).forEach(key => {
      if (key.startsWith("x-ms-")) {
          canonicalHeaderNames.push(key);
      }
    });
    // Sort headers lexographically by name
    canonicalHeaderNames.sort();

    const canonicalHeaderParts = [];
    canonicalHeaderNames.forEach(key => {
      let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];

      // Populate variables
      value = pm.environment.replaceIn(value);

      // Replace whitespace in value but not if its within quotes
      if (!value.startsWith("\"")) {
        value = value.replace(/\s+/, " ");
      }

      canonicalHeaderParts.push(`${key}:${value}`);
    });

    // Add headers to signature
    signatureParts.push.apply(signatureParts, canonicalHeaderParts);

    // Construct CanonicalizedResource
    const canonicalResourceParts = [
      `/${pm.environment.get("storeAcc")}${pm.request.url.getPath()}`
    ];
    const canonicalQueryNames = [];
    pm.request.url.query.each(query => {
      canonicalQueryNames.push(query.key.toLowerCase());
    });
    canonicalQueryNames.sort();
    canonicalQueryNames.forEach(queryName => {
      const value = pm.request.url.query.get(queryName);

      // NOTE: This does not properly explode multiple same query params' values
      // and turn them into comma-separated list
      canonicalResourceParts.push(`${queryName}:${value}`);
    });
    // Add resource to signature
    signatureParts.push.apply(signatureParts, canonicalResourceParts);

    console.log("Signature Parts", signatureParts);
 
    // Now, construct signature raw string
    const signatureRaw = signatureParts.join("\n");

    console.log("Signature String", JSON.stringify(signatureRaw));

    // Hash it using HMAC-SHA256 and then encode using base64
    const storageKey = pm.variables.get("accountKey");
    const signatureBytes = CryptoJS.HmacSHA256(signatureRaw,    CryptoJS.enc.Base64.parse(storageKey));
    console.log("signatureBytes",signatureBytes);
    const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);

    console.log("signatureEncoded",signatureEncoded);
    console.log("Storage Account", pm.environment.get("storeAcc"));
    console.log("Storage Key", storageKey);

    // Finally, make it available for headers
    pm.environment.set("sigStr",
       `SharedKey ${pm.environment.get("storeAcc")}:${signatureEncoded}`); 

This code works and the signature i generate is authenticated and i get access to blob apis.

but i implemented the same code in java and every time it gives me the same problem "403 Authentication error."

Java code

    public String generateOauthHeader(String method, UserDetails userFields, String baseUrl, String azureFileListUrl)  {

        String key = userFields.getAccountKey();

        DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ");
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        String startDate = dateFormat.format(new Date());
        
        String stringToSign = method + "\\n\\n\\n\\n\\napplication/json\\n\\n\\n\\n\\n\\n\\nx-ms-date:" + "Tue, 05 Jul 2022 11:45:00 "
                + "GMT\\nx-ms-version:2020-04-08\\n/" + userFields.getAccountName() + "/"
                + userFields.getContainerName();

        if (null != azureFileListUrl) {
            azureFileListUrl =azureFileListUrl.replace("?", "");
            azureFileListUrl =azureFileListUrl.replace("=", ":");
            String[] paramlist = azureFileListUrl.split("&");

            stringToSign = stringToSign + "\\n" + paramlist[1] + "\\n" + paramlist[0];
        }
        stringToSign="\""+stringToSign+"\"";
        System.err.println("Signature string  =  " + stringToSign);
        String signature = getHMAC256(key, stringToSign);

        signature = "SharedKey " + userFields.getAccountName() + ":" + signature;
        System.err.println("Final Signature string  =  " + signature);
        return signature;
    }

    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(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signature;
    }

I am adding below two string which are generated around same time.

The sample signatureString before signing are identical. I have printed and checked them as well.

postman= "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:45:00 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"

code = "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:44:56 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"

EDIT:
here are the headers sent with the request.

      requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");
      requestHeaders.put("x-ms-version", "2020-04-08");
      requestHeaders.put("Content-Type", "application/json");
      requestHeaders.put("Authorization", "SharedKey accountName:yvfdsfdssdfF0lGGtEWGU+b7BqFY0UHMkvI=
");

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

花间憩 2025-02-20 04:37:07

我相信问题是以下代码行:

requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");

本质上是在您的请求标题中,您将日期发送为tue,05 Jul 2022 11:45:00 GMT gmt ,但是在您的StringToSign中< /code>您将日期发送为TUE,05 JUL 2022 11:44:56 GMT in X-MS-DATE。这将导致您要发送的StringTosign,并且服务器计算为不匹配,从而导致不同的授权标题值。

要解决此问题,请确保X-MS-DATE标题的值在StringTosign和您的请求标头中都是相同的。我建议将日期对象传递到generateOauthheader方法,并在您的请求标头中使用X-MS-DATE的同一日期对象。

I believe the issue is with the following line of code:

requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");

Essentially in your request headers, you are sending the date as Tue, 05 Jul 2022 11:45:00 GMT however in your stringToSign you are sending the date as Tue, 05 Jul 2022 11:44:56 GMT in x-ms-date. This would cause the stringToSign that you are sending and the one computed by the server to mismatch and thus resulting in different authorization header values.

To fix this, please ensure that the value for x-ms-date header is the same in both stringToSign and your request headers. I would recommend passing the date object to generateOauthHeader method and use the same date object in your request headers for x-ms-date.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文