DocumentDB REST API - 授权令牌错误

Gra*_*ham 3 azure azure-functions azure-cosmosdb

问题

每当我们请求列表或查询时,我们都会看到从DocumentDB REST API返回此错误,但是当我们按名称/ id获取对象时,我们看到错误:

输入授权令牌无法提供请求.请检查是否按照协议构建了预期的有效负载,并检查所使用的密钥.

背景

我们已成功将node.js sdk与DocumentDB一起使用了一年多,但由于我们希望将后端的restful API代码从node.js App Service迁移到Azure Functions,我们看到10-30秒的滞后时间因为在一段时间内没有调用Function时,DocumentDB sdk加载缓慢.我们知道Function实例很热,并且这不是基于以前与Azure Functions团队通信的冷实例问题.

为了解决这个问题,我们想要测试DocumentDB REST API,它需要零个外部库在node.js函数中运行,并且应该尽快执行.

这是在本地node.js中运行的测试工具.一旦它运行,我们将把它移动到Azure功能.

var express = require('express');
var router = express.Router();
var crypto = require("crypto"); 
var request = require('request');

router.get('/', function (req, res, next) {

  var key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
  var uri = "https://xxxxxx.documents.azure.com";

  var verb = 'GET';
  var type = 'dbs';
  var link = 'dbs';
  var url = `${uri}/${link}`;

  var headers = getDefaultRequestHeaders();

  // var body = `{"query":"SELECT * FROM c", "parameters": []}`;
  var body = '';

  headers['content-length'] = body.length;
  headers['authorization'] = getAuthorizationTokenUsingMasterKey(verb, type, link, headers['x-ms-date'], key);

  request[verb.toLowerCase()]({"url": url, "headers": headers, "body": body}, function (error, response, body) {

    // console.log(`error is ${error}`);
    // console.log(`response is ${JSON.stringify(response, null, 2)}`);
    console.log(`body is ${body}`);

    res.status(response.statusCode).json(body);

  });

});

function getDefaultRequestHeaders(isQuery, date) {

  var headers = {
    "content-type": "application/json", 
    "x-ms-date": new Date().toUTCString(),
    "x-ms-version": "2017-02-22",
    "accept": "application/json",
    "cache-control": "no-cache",
    "user-agent": "xxxxxx/1.0"
  };

  if(isQuery) {
    headers["x-ms-documentdb-isquery"] = true;
    headers["content-type"] = "application/query+json";
  }

  if(date) {
    headers["x-ms-date"] = date;
  }

  return headers;

}

function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceLink, date, masterKey) {  
    var key = new Buffer(masterKey, "base64");  

    var text = (verb || "").toLowerCase() + "\n" +   
               (resourceType || "").toLowerCase() + "\n" +   
               (resourceLink || "") + "\n" +   
               date.toLowerCase() + "\n" +   
               "" + "\n";  

    var body = new Buffer(text, "utf8");  
    var signature = crypto.createHmac("sha256", key).update(body).digest("base64");  

    var MasterToken = "master";  

    var TokenVersion = "1.0";  

    return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);  
}  


module.exports = router;
Run Code Online (Sandbox Code Playgroud)

我们从DocumentDB API页面中的Access控件逐字使用getAuthorizationTokenFromMasterKey函数.

密钥,应用程序名称和用户代理已被x替换为隐私/安全性.

检测结果

列出数据库

当我尝试列出dbs的最基本调用时,服务器返回令牌错误:

  var verb = 'GET';
  var type = 'dbs';
  var link = 'dbs';
Run Code Online (Sandbox Code Playgroud)

响应:

"{\"code \":\"Unauthorized \",\"message \":\"输入授权令牌无法提供请求.请检查是否按照协议构建了预期的有效负载,并检查所使用的密钥.服务器使用以下有效负载进行签名:'get \ndbs \n \nsat,12 aug 2017 12:28:41 gmt \n \n'\ r \nActivityId:acbf19d9-6485-45c5-9c30-6aa21f14d5b3 \"}"

获取数据库

但是,当我执行获取数据库请求时它工作正常:

  var verb = 'GET';
  var type = 'dbs';
  var link = 'dbs/00001';
Run Code Online (Sandbox Code Playgroud)

响应:

"{\" ID\":\" 00001\"\ "_摆脱\":\ "0eUiAA == \",\ "_ TS \":1441256154,\ "_自我\":\" DBS\/ 0eUiAA ==\/ \",\ "_ ETAG \":\ "\\" 00007d4a-0000-0000-0000-55e7d2da0000 \\ "\",\ "_ colls \":\ "colls\/ \",\ "_用户\" :\ "用户\/\"}"

列表集合

同样,从此数据库请求集合列表会返回令牌错误:

var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls';
Run Code Online (Sandbox Code Playgroud)

Respose:

"{\"code \":\"Unauthorized \",\"message \":\"输入授权令牌无法提供请求.请检查是否按照协议构建了预期的有效负载,并检查所使用的密钥.服务器使用以下有效负载进行签名:'get \ncolls \ndbs/00001 \nsat,12 Aug 2017 12:32:19 gmt \n \n'\ r \nActivityId:8a9d4ff8-24ef-4fd2-b400-f9f8aa743572 \"} "

获取收藏

但是当我调用get collection时,我得到了一个有效的响应:

var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls/00001';
Run Code Online (Sandbox Code Playgroud)

响应:

"{\" ID\":\" 00001 \",\ "indexingPolicy \":{\ "indexingMode \":\ "一致\"\"自动\":真,\ "includedPaths \":[{\ "路径\":\ "\/*\",\ "指数\":[{\ "之类的\":\ "范围\",\ "数据类型\":\ "号\",\ "精\" :-1},{\ "样\":\ "范围\",\ "数据类型\":\ "字符串\",\ "精密\": - 1},{\ "样\":\"空间\",\ "数据类型\":\ "点\"}]}],\ "excludedPaths \":[]},\ "_ RID \":\ "0eUiAJMAdQA = \"\"_ TS \":1454200014,\"_自\":\ "DBS\/ 0eUiAA ==\/ colls\/ 0eUiAJMAdQA =\/ \",\ "_ ETAG \":\ "\\" 00000100-0000-0000-0000-56ad54ce0000 \\"\ "\"_文档\":\ "文档\/\",\ "_存储过程\":\ "存储过程\/\",\ "_触发\":\ "触发器\/\",\ "_的UDF \":\"的UDF\/ \",\ "_冲突\":\ "矛盾\/\"}"

列出文件

请求该集合上的列表文档给出了这个错误:

var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
Run Code Online (Sandbox Code Playgroud)

响应:

"{\"code \":\"Unauthorized \",\"message \":\"输入授权令牌无法提供请求.请检查是否按照协议构建了预期的有效负载,并检查所使用的密钥.服务器使用以下有效负载进行签名:'get \ndocs \ndbs/00001/colls/00001 \nsat,12 aug 2017 12:34:48 gmt \n \n'\ r \nActivityId:57097e95-c41b-4770-b91a- 370418ef2cce\"}"

获取文件

毫不奇怪,获取单个文档可以正常工作:

var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs/e7fe638d-2152-2097-f9c6-9801d7cf5cdd';
Run Code Online (Sandbox Code Playgroud)

响应:

"{\"name \":\"test rest api \",\"id \":\"e7fe638d-2152-2097-f9c6-9801d7cf5cdd \",\"_ rid \":\"0eUiAJMAdQCbHgAAAAAAAA == \",\"_自\":\ "DBS\/ 0eUiAA ==\/ colls\/ 0eUiAJMAdQA =\/文档\/0eUiAJMAdQCbHgAAAAAAAA ==\/ \",\ "_ ETAG \":\ "\\" 0d00d1ee-0000-0000 -0000-598ef7d40000 \\ "\"\"_附件\":\ "附件\/\",\ "_ TS \":1502541779}"

查询文件

最后,发送查询也会导致令牌错误:

var verb = 'POST';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
var body = `{"query":"SELECT * FROM c", "parameters": []}`;
Run Code Online (Sandbox Code Playgroud)

响应:

"{\"code \":\"Unauthorized \",\"message \":\"输入授权令牌无法提供请求.请检查是否按照协议构建了预期的有效负载,并检查所使用的密钥.服务器使用以下有效负载进行签名:'post \ndocs \ndbs/00001/colls/00001 \nsat,12 Aug 2017 12:35:42 gmt \n \n'\ r \nActivityId:b8b95f8c-1339-423e-b0e7- 0d15d3056180\"}"

Gau*_*tri 6

我认为文档不正确.在他们说的地方resourceLink,他们应该说resource id.如果您查看Node SDK代码,这就是他们计算授权标头的方式(请注意使用resourceId):

getAuthorizationTokenUsingMasterKey: function (verb, resourceId, resourceType, headers, masterKey) {
    var key = new Buffer(masterKey, "base64");

    var text = (verb || "").toLowerCase() + "\n" +
               (resourceType || "").toLowerCase() + "\n" +
               (resourceId || "") + "\n" +
               (headers["x-ms-date"] || "").toLowerCase() + "\n" +
               (headers["date"] || "").toLowerCase() + "\n";

    var body = new Buffer(text, "utf8");

    var signature = crypto.createHmac("sha256", key).update(body).digest("base64");

    var MasterToken = "master";

    var TokenVersion = "1.0";

    return "type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature;
},
Run Code Online (Sandbox Code Playgroud)

因此,如果要列出数据库,因为没有资源ID,您需要为link变量使用空字符串.同样,如果要在数据库中列出集合,则链接实际上应该是数据库的id(例如dbs/00001,不是dbs/00001/colls).