Azure表存储查询失败,并在Windows应用商店应用中出现AuthenticationFailed错误

时间:2013-06-05 14:54:09

标签: node.js azure windows-8 winjs azure-table-storage

azure table query rest api失败并出现AuthenticationFailed错误。

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
  <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
      <code>AuthenticationFailed</code>
      <message xml:lang="en-US">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.</message>
   </error>

要形成并进行其余调用的winjs应用代码段是:

var date = new Date().toGMTString().replace('UTC', 'GMT');
var xhrOption = {
    type: 'GET',
    url: url,
    headers: {
        'content-type': 'application/atom+xml;charset="utf-8"',
        'content-length': 0,
        dataserviceversion: '1.0;NetFx',
        maxdataserviceversion: '2.0;NetFx',
        'x-ms-version': '2011-08-18',
        'x-ms-date': date,
        accept: 'application/atom+xml,application/xml',
        'Accept-Charset': 'UTF-8',
    },
};

xhrOption.headers.Authorization = AuthorizationHeader().computeForTableService(options, xhrOption);

计算授权标头的代码很长。它列在下面:

_getSignatureStringForTableService: function getSignatureStringForTableService()
{
    var headers = this.xhrOptions.headers;
    var httpVerb = this.xhrOptions.type.toUpperCase();
    var sigItems = [];
    sigItems.push(httpVerb);
    var contentMD5 = this._getHeaderOrDefault(headers, 'Content-MD5');
    sigItems.push(contentMD5);
    var contentType = this._getHeaderOrDefault(headers, 'content-type');
    sigItems.push(contentType);
    var date = this._getHeaderOrDefault(headers, 'x-ms-date');
    if (!date)
        date = this._getHeaderOrDefault(headers, 'Date');
    sigItems.push(date);
    var canonicalizedResource = this._getCanonicalizedResource();
    sigItems.push(canonicalizedResource);
    var result = sigItems.join('\n');
    return result;
},
_getCanonicalizedResource: function getCanonicalizedResource()
{
    var items = [];
    var path;
    if (config.storageAccount.isDevStorage)
        path = "/" + config.storageAccount.name + '/' + config.storageAccount.name;
    else
        path = "/" + config.storageAccount.name;

    path += "/" + this.options.resourcePath;
    items.push(path);
    var result = items.join('\n');
    return result;
},
computeForTableService: function computeForTableService(options, xhrOptions)
{
    this.options = options;
    this.xhrOptions = xhrOptions;
    var sig = this._computeSignatureForTableService();
    var result = 'SharedKey ' + config.storageAccount.name + ':' + sig;
    return result;
},
_computeSignatureForTableService: function computeSignatureForTableService()
{
    var sigString = this._getSignatureStringForTableService();

    // TODO: use crypto from windows api. currently uses, google cryptoJS lib
    var key = CryptoJS.enc.Base64.parse(config.storageAccount.primaryKey);
    var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
    hmac.update(sigString);
    var hash = hmac.finalize();
    var result = hash.toString(CryptoJS.enc.Base64);
    return result;
},

有趣的是,我让整个代码在2天前正常运行。我已更新服务代码以使用更新的azure nodejs sdk。我想知道更新是否在发布者/消费者代码中引起了一些不确定?

其他观察

  1. 使用azure nodejs模块的服务代码能够无错误地查询表存储。
  2. 我通过azure nodejs模块进行了调试,查看了stringToSign并与winjs代码生成的内容进行了匹配。两者都是相同的。
  3. 服务升级为使用0.10.x节点和相应的最新azure nodejs sdk。
  4. 示例:stringToSign

    GET\n\napplication/atom+xml;charset="utf-8"\nWed, 5 Jun 2013 14:43:30 GMT\n/devstoreaccount1/devstoreaccount1/mytable()

  5. 感谢您详细了解。

1 个答案:

答案 0 :(得分:1)

最后 - 错误的根本原因已经消失。问题是x-ms-date标题的价值。

预期价值 - Thu, 06 Jun 2013 08:09:50 GMT 在上面的代码中计算的值 - Thu, 6 Jun 2013 08:20:34 GMT

日期之前丢失的0是此错误的根本原因。因此,用于计算授权标头的stringToSign不正确。因此,授权标头不正确导致AuthenticationFailed错误。这也解释了为什么这段代码工作几天的原因(5月底 - 日期有两位数)。

如果来自MS的某人正在阅读此内容,那么获得正确数量的详细信息以及错误代码将非常有用。仅AuthenticationFailed错误代码并未向开发人员提供任何线索。

我之前使用过azure storage blob rest api。它为相同的AuthenticationFailed错误代码返回更好的错误。它通过expected stringToSignfound stringToSign以及AuthenticationFailed错误代码发送。它是非常有用的,几分钟就可以解决bug。

来自Microsoft的Network monitor。编写了c#代码片段,使用azure .net sdk进行azure表查询,并逐个字符地比较每个标题以解决问题。