通过 REST 在 DocumentDb 中创建文档时未经授权
Unauthorized when creating a document in DocumentDb via REST
我使用的是 .NET Core,所以无法使用 Azure DocumentDb SDK。这就是我想通过 REST 接口创建文档的原因。我设法查询了数据库,但是当我 POST 一个 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));
}
我确定数据库 ID、collectionid、url 和密钥是正确的。密钥和 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"
}
关于我做错了什么或我如何分析这个的任何提示?
请注意,有两种方法可以在 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 区分大小写。例如,如果集合的资源 ID 是 MyCollection,则资源 ID 应该恰好是串联令牌中的 MyCollection。
本文提供了构建哈希签名令牌的更多详细信息。
https://msdn.microsoft.com/en-us/library/azure/dn783368.aspx
这是一个微妙的问题,但我使用以下内容作为资源 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
但是,当我这样做时,我得到了一个方法不允许的响应。这是因为我也将此 link 用于 post uri。但是 post uri 应该是包含 /docs
的 link。
所以我的完整代码现在变成了:
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);
}
我使用的是 .NET Core,所以无法使用 Azure DocumentDb SDK。这就是我想通过 REST 接口创建文档的原因。我设法查询了数据库,但是当我 POST 一个 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));
}
我确定数据库 ID、collectionid、url 和密钥是正确的。密钥和 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"
}
关于我做错了什么或我如何分析这个的任何提示?
请注意,有两种方法可以在 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 区分大小写。例如,如果集合的资源 ID 是 MyCollection,则资源 ID 应该恰好是串联令牌中的 MyCollection。
本文提供了构建哈希签名令牌的更多详细信息。 https://msdn.microsoft.com/en-us/library/azure/dn783368.aspx
这是一个微妙的问题,但我使用以下内容作为资源 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
但是,当我这样做时,我得到了一个方法不允许的响应。这是因为我也将此 link 用于 post uri。但是 post uri 应该是包含 /docs
的 link。
所以我的完整代码现在变成了:
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);
}