简体   繁体   中英

Cosmos DB Azure Table API oData Authentication REST / C#?

I'm trying to access Azure Cosmos DB using Table API.

The challenge is, despite creating SharedKeyLite, server is still returning Unauthorized - seems like SharedKeyLite is not supported or I'm generating the signature or headers wrong.

Here is the code

    static readonly string storageAccountName = "accountName";
    static readonly string storageAccountKey = "xxxx";
    static readonly string uri = "https://accountName.table.cosmosdb.azure.com/Contacts()";
    static readonly string utc_date = DateTime.UtcNow.ToString("r");


    static void Main(string[] args)
    {

        Console.WriteLine(GetResult().Result);

    }


    static async Task<string> GetResult()
    {
        // Set this to whatever payload you desire. Ours is null because 
        //   we're not passing anything in.
        Byte[] requestPayload = null;

        var requestDateString = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
        var requestUri = new Uri(uri);

        DateTime now = DateTime.UtcNow;
        //Instantiate the request message with a null payload.
        using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri)
        { Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) })
        {

            ConstructHeaders(httpRequestMessage.Headers, requestDateString);

            string authorizationHeader = GenerateSharedKeyLite(storageAccountKey, storageAccountName, uri,requestDateString);
            httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("SharedKeyLite", authorizationHeader);
            // Send the request.
            using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
            {
                string json = await httpResponseMessage.Content.ReadAsStringAsync();
                return json;
            }
        }
    }

These are the headers I"m adding, expansion of ConstructHeaders method. Refer this link for request parameters

     //Construct the headers
    static void ConstructHeaders(HttpRequestHeaders headers, string now)
    {

        headers.Add("x-ms-date", now);
        headers.Add("x-ms-version", "2017-04-17");
        // If you need any additional headers, add them here before creating
        //   the authorization header. 
        headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));


        if (headers.Contains("DataServiceVersion"))
            headers.Remove("DataServiceVersion");
        headers.Add("DataServiceVersion", "3.0;NetFx");
        if (headers.Contains("MaxDataServiceVersion"))
            headers.Remove("MaxDataServiceVersion");
        headers.Add("MaxDataServiceVersion", "3.0;NetFx");
    }

And this is the method that creates the SharedKeyLite

    //Created Shared Key Lite 
    static string GenerateSharedKeyLite(string accessKey, string account, string url, string date)
    {
        var uri = new Uri(url);

        var canonicalizedResourceString = uri.PathAndQuery;
        var queryStart = canonicalizedResourceString.IndexOf('?');
        if (queryStart > -1)
        {
            if (queryStart < canonicalizedResourceString.Length - 1)
            {
                var path = canonicalizedResourceString.Substring(0, queryStart);
                var parameters = HttpUtility.ParseQueryString(canonicalizedResourceString.Substring(queryStart + 1));
                var sb = new StringBuilder();
                foreach (var keyOri in parameters.Keys)
                {
                    var value = parameters[keyOri];
                    var key = keyOri.ToLowerInvariant();
                    sb.Append("\n");
                    sb.Append(key);
                    sb.Append(":");
                    sb.Append(value);
                }
                canonicalizedResourceString = canonicalizedResourceString + sb.ToString();
            }
            else
            {
                canonicalizedResourceString = canonicalizedResourceString.Substring(0, canonicalizedResourceString.Length - 1);
            }
        }
        canonicalizedResourceString = $"/{account}{canonicalizedResourceString}";

        var stringToSign = $"{date}\n{canonicalizedResourceString}";
        var signedSignature = string.Empty;
        using (var hmac = new HMACSHA256(Convert.FromBase64String(accessKey)))
        {
            var outputBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
            var signature = Convert.ToBase64String(outputBytes);
            return $"{account}:{signature}";
        }


    }

Any Help? Ideally I want to perform the odata query using simple.odata, but first trying to make this work using HttpClient

Just copy your code and it works on my side. If you haven't modified your code, please make sure your storageAccountName and storageAccountKey are correct.

BTW, in method GenerateSharedKeyLite there's no need to add query parameters to canonicalizedResourceString for entity operation. You only need to add comp if you want to operate component info for table or service. See constructing-the-canonicalized-resource-string .

The query string should include the question mark and the comp parameter (for example, ?comp=metadata ). No other parameters should be included on the query string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM