在 cosmos db 中创建资源令牌,当使用该资源令牌进行 REST API 调用时,CosmosDb 会给出 403 Unauthorized 错误。 相同的资源令牌在 CosmosDb 中使用时不需要 sdk,它可以工作。 所以我执行 REST API 调用的方式肯定有问题。 这是代码。
using Microsoft.Azure.Cosmos;
using System.Net.Http.Formatting;
namespace CosmosDb_Rest_DotNet
{
internal class Program
{
static readonly string endpoint = "https://localhost:8081/";
static readonly string masterKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
static readonly Uri baseUri = new Uri(endpoint);
static readonly string databaseId = "SampleDb";
static readonly string collectionId = "Lists";
static readonly string documentId = "cbd77fac-f7f0-46fb-a2f3-f6b48c11670c";
static readonly string partitionKey = "MyPartitionKey";
static readonly string userId = "userId";
static readonly string utc_date = DateTime.UtcNow.ToString("r");
static void Main(string[] args)
{
Console.WriteLine(DateTime.UtcNow.ToString("r"));
string resourceToken = "";
using (var cosmosClient = new CosmosClient(endpoint, masterKey))
{
Database cosmosdb = cosmosClient.GetDatabase(databaseId);
var listsContainer = cosmosdb.GetContainer(collectionId);
//Create user
try
{
var result = cosmosdb.CreateUserAsync(userId).Result;
}
catch (Exception)
{
//user already exists
}
var cosmosUser = cosmosdb.GetUser(userId);
Console.WriteLine($"Created Cosmos User: {cosmosUser.Id}");
//create permission and get ResourceToken. Resource Token permission is restricted to Partition Key
var listsContainerPermissionProps = new PermissionProperties($"{listsContainer.Id}-{cosmosUser.Id}",
PermissionMode.All,
listsContainer,
new PartitionKey(partitionKey));
var listsContainerPermissionResponse = cosmosUser.UpsertPermissionAsync(listsContainerPermissionProps, (int)TimeSpan.FromMinutes(30).TotalSeconds).Result;
resourceToken = listsContainerPermissionResponse.Resource.Token;
Console.WriteLine("Successfully received Resource Token.");
}
Console.WriteLine("Using Resource Token in CosmosClient SDK to query document.");
//TEST resourceToken using Cosmos SDK
using (CosmosClient resoureceTokenCosmosClient = new CosmosClient(endpoint, resourceToken))
{
Database resourceTokenCosmosDb = resoureceTokenCosmosClient.GetDatabase(databaseId);
var resourceTokenListsContainer = resourceTokenCosmosDb.GetContainer(collectionId);
var query = new QueryDefinition("SELECT * FROM c where c.Aid = 'MyPartitionKey'");
var queryResponse = resourceTokenListsContainer.GetItemQueryIterator<dynamic>(query, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("MyPartitionKey") });
while (queryResponse.HasMoreResults)
{
var items = queryResponse.ReadNextAsync().Result;
foreach (var item in items)
{
Console.WriteLine($"Received Item using CosmosClient with resource token: Name: {item.Name}, id: {item.id}");
}
}
Console.WriteLine("Resource Token Successfully worked and returned documents");
}
Console.WriteLine("Using same Resource Token in REST API Test");
//REST Client TEST
using (var client = new System.Net.Http.HttpClient())
{
string response = string.Empty;
string authHeader = string.Empty;
string verb = string.Empty;
string resourceType = string.Empty;
string resourceId = string.Empty;
string resourceLink = string.Empty;
client.DefaultRequestHeaders.Add("x-ms-date", DateTime.UtcNow.ToString("r"));
client.DefaultRequestHeaders.Add("x-ms-version", "2020-07-15");
client.DefaultRequestHeaders.Add("x-ms-documentdb-isquery", "True");
try
{
//EXECUTE a query
verb = "POST";
resourceType = "docs";
resourceLink = string.Format("dbs/{0}/colls/{1}/docs", databaseId, collectionId);
//url encode resource token
authHeader = Uri.EscapeDataString(resourceToken);
client.DefaultRequestHeaders.Remove("authorization");
client.DefaultRequestHeaders.Add("authorization", authHeader);
var qry = new SqlQuerySpec { query = "SELECT * FROM c where c.Aid = 'MyPartitionKey'" };
var r = client.PostWithNoCharSetAsync(new Uri(baseUri, resourceLink), qry).Result;
Console.WriteLine(r.Content.ReadAsStringAsync().Result);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
private static string GenerateMasterKeyAuthorizationSignature(string verb, string resourceId, string resourceType, string key, string keyType, string tokenVersion)
{
var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) };
string payLoad = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}\n{1}\n{2}\n{3}\n{4}\n",
verb.ToLowerInvariant(),
resourceType.ToLowerInvariant(),
resourceId,
utc_date.ToLowerInvariant(),
""
);
byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));
string signature = Convert.ToBase64String(hashPayLoad);
return System.Web.HttpUtility.UrlEncode(String.Format(System.Globalization.CultureInfo.InvariantCulture, "type={0}&ver={1}&sig={2}",
keyType,
tokenVersion,
signature));
}
}
//This is used when executing a query via REST
//DocumentDB expects a specific Content-Type for queries
//When setting the Content-Type header it must not have a charset, currently.
//This custom class sets the correct Content-Type and clears the charset
public class NoCharSetJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
public override void SetDefaultContentHeaders(Type type, System.Net.Http.Headers.HttpContentHeaders headers, System.Net.Http.Headers.MediaTypeHeaderValue mediaType)
{
base.SetDefaultContentHeaders(type, headers, new System.Net.Http.Headers.MediaTypeHeaderValue("application/query+json"));
headers.ContentType.CharSet = "";
}
}
//A custom extension of HttpClient that adds a new PostWithNoCharSet async method
//that uses the custom MediaTypeFormatter class to post with the correct Content-Type header
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PostWithNoCharSetAsync<T>(this HttpClient client, Uri requestUri, T value) { return await client.PostAsync(requestUri, value, new NoCharSetJsonMediaTypeFormatter()); }
}
class SqlQuerySpec
{
public string query { get; set; }
}
}
这是输出
Created Cosmos User: userId
Successfully received Resource Token.
Using Resource Token in CosmosClient SDK to query document.
Received Item using CosmosClient with resource token: Name: Test Doc, id: cbd77fac-f7f0-46fb-a2f3-f6b48c11670c
Resource Token Successfully worked and returned documents
Using same Resource Token in REST API Test
{"code":"Forbidden","message":"Insufficient permissions provided in the authorization header for the corresponding request. Please retry with another authorization header.\r\nActivityId: 723c8bc1-2d11-463e-a5bc-7f3ef038e59f, Microsoft.Azure.Documents.Common/2.14.0"}
任何人都应该能够运行本地 cosmos db 模拟器的代码。
当调用rest api端点时,授权标头不仅仅是资源令牌。详情请参考此链接:
它应该是这样的格式: 类型=资源&ver=1.0&sig=5mDuQBYA0kb70WDJoTUzSBMTG3owkC0/cEN4fqa18/s=