通过REST在DocumentDb中创建文档时未经授权

时间:2016-04-20 18:26:01

标签: .net rest azure azure-cosmosdb .net-core

我正在使用.NET Core,因此我无法使用Azure DocumentDb SDK。这就是我想通过REST接口创建文档的原因。我已经设法查询数据库,但是当我发布JSON文档时,我收到了一个未经授权的响应。这是我的代码:

const string DatabaseId = "DB-id";
const string CollectionId = "UserSettings";
var documentDbUrl = "Injected via DI";
var authorizationKey = "Also injected;

using (var httpClient = new HttpClient())
{
    var utcNow = DateTime.UtcNow;
    httpClient.DefaultRequestHeaders.Add("x-ms-date", utcNow.ToString("r"));
    httpClient.DefaultRequestHeaders.Add("x-ms-version", "2015-08-06");
    httpClient.DefaultRequestHeaders.Add("x-ms-documentdb-is-upsert", "true");

    var resourceLink = string.Format("dbs/{0}/colls/{1}/docs", DatabaseId, CollectionId);
    var baseUrl = new Uri(documentDbUrl);

    var masterKeyAuthorizationSignatureGenerator = new MasterKeyAuthorizationSignatureGenerator();
    var authHeader = masterKeyAuthorizationSignatureGenerator.Generate("POST", resourceLink, "docs", authorizationKey, "master", "1.0", utcNow);
    httpClient.DefaultRequestHeaders.Add("authorization", authHeader);

    var response = await httpClient.PostAsJsonAsync(new Uri(baseUrl, resourceLink), userSettings);

    // at this point, response.StatusCode is Unauthorized
}

您看到的MasterKeyAuthorizationSignatureGenerator包含the logic to create the hash,在查询数据库时有效:

public string Generate(string verb, string resourceId, string resourceType, string key, string keyType, string tokenVersion, DateTime requestDateTime)
{
    var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };

    var payLoad = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}\n{1}\n{2}\n{3}\n{4}\n",
            verb.ToLowerInvariant(),
            resourceType.ToLowerInvariant(),
            resourceId,
            requestDateTime.ToString("r").ToLowerInvariant(),
            ""
    );

    var hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));
    var signature = Convert.ToBase64String(hashPayLoad);

    return WebUtility.UrlEncode(string.Format(System.Globalization.CultureInfo.InvariantCulture, "type={0}&ver={1}&sig={2}",
        keyType,
        tokenVersion,
        signature));
}

我确定databaseid,collectionid,url和key是正确的。密钥和id是我用来查询的,它可以工作。当我更改url时,例如通过添加documentid(然后我自己生成),我得到另一条消息(MethodNotAllowed)。

更新

使用Postman,我可以看到这是我得到的回应:

{
    "code": "Unauthorized",
    "message": "The input authorization token can't serve the request. 
                Please check that the expected payload is built as per the 
                protocol, and check the key being used. Server used the 
                following payload to sign: 
                'post\ndocs\ndbs/MyDb/colls/MyColl\nsat, 23 apr 2016 09:44:39 gmt\n\n'\r\nActivityId: 1be76530-ad32-4b54-b96b-6e0d4ebbc851"
}

关于我做错了什么或者我如何分析它的任何提示?

2 个答案:

答案 0 :(得分:0)

请注意,有两种方法可以在URI中指定资源:

1) using the resource id.  The id is user settable.

2) using the resource _rid.  This is the system generated id for the resource.

使用资源ID(方法1)时,必须确保用于散列签名令牌的连接令牌中的资源ID与资源中使用的外壳相同,因为资源ID区分大小写。例如,如果资源标识是集合的MyCollection,那么资源标识应该是连接标记中的MyCollection。

本文提供了构造哈希签名令牌的其他详细信息。 https://msdn.microsoft.com/en-us/library/azure/dn783368.aspx

答案 1 :(得分:0)

这是一个微妙的,但我使用以下作为资源ID来生成我的哈希:

var resourceLink = string.Format("dbs/{0}/colls/{1}/docs", DatabaseId, CollectionId);
// this becomes something like dbs/MyDb/colls/MyCollection/docs

/docs应该省略。所以它变成了:

var resourceLink = string.Format("dbs/{0}/colls/{1}", DatabaseId, CollectionId);
// this becomes something like dbs/MyDb/colls/MyCollection

然而,当我这样做时,我得到一个方法不允许响应。这是因为我也把这个链接用于后uri。但是帖子uri应该是包含/docs的链接。

所以现在我的完整代码变成了:

const string DatabaseId = "DB-id";
const string CollectionId = "UserSettings";
var documentDbUrl = "Injected via DI";
var authorizationKey = "Also injected;

using (var httpClient = new HttpClient())
{
    var utcNow = DateTime.UtcNow;
    httpClient.DefaultRequestHeaders.Add("x-ms-date", utcNow.ToString("r"));
    httpClient.DefaultRequestHeaders.Add("x-ms-version", "2015-08-06");
    httpClient.DefaultRequestHeaders.Add("x-ms-documentdb-is-upsert", "true");

    var resourceLink = string.Format("dbs/{0}/colls/{1}", DatabaseId, CollectionId);
    var baseUrl = new Uri(documentDbUrl);

    var masterKeyAuthorizationSignatureGenerator = new MasterKeyAuthorizationSignatureGenerator();
    var authHeader = masterKeyAuthorizationSignatureGenerator.Generate("POST", resourceLink, "docs", authorizationKey, "master", "1.0", utcNow);
    httpClient.DefaultRequestHeaders.Add("authorization", authHeader);

    var response = await httpClient.PostAsJsonAsync(new Uri(baseUrl, resourceLink + "/docs"), userSettings);
}