首页 > 解决方案 > C# RestClient 调用 magento api 返回 401 签名无效

问题描述

我试图从 c# RestClient 调用 magento 上的某些特定端点,但没有运气。通话从这里开始:

private void SendDeletedProduct(string itemCode)
    {
        string consumerKey = "....."; 
        string consumerSecret = ".....";
        string tokenSecret = ".....";
        string tokenValue = ".....";
        string url = string.Format("http://.........:8090/api/rest/pylon/{0}", itemCode);

        var client = new RestClient(url);
        client.Timeout = -1;
        var request = new RestRequest(Method.PUT);
        request.AddHeader("Content-Type", "application/json");
        request.AddHeader("Authorization", GetHeader("DELETE", consumerKey, consumerSecret, tokenSecret, tokenValue, url));

        IRestResponse response = client.Execute(request);
        if (response.StatusCode != HttpStatusCode.OK)
        {
            LogError(response, string.Empty, itemCode, "Product", "DELETE");
        }
    }

并在此处生成标头:

private string GetHeader(string method, string cKey, string cSecret, string tSecret, string tValue, string apiUrl)
    {
        Dictionary<string, string> oauthD = new Dictionary<string, string>();
        oauthD.Add("consumerKey", cKey);
        oauthD.Add("consumerSecret", cSecret);
        oauthD.Add("tokenSecret", tSecret);
        oauthD.Add("tokenValue", tValue);
        oauthD.Add("url", apiUrl.Replace(":8090", string.Empty));

        var timeStamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
        var nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(timeStamp));

        var signatureBaseString = HttpUtility.UrlEncode(method + "&");
        signatureBaseString += HttpUtility.UrlEncode(oauthD["url"] + "&");
        signatureBaseString += HttpUtility.UrlEncode(
                "oauth_consumer_key=" + oauthD["consumerKey"] + "&" +
                "oauth_nonce=" + nonce + "&" +
                "oauth_signature_method=HMAC-SHA1" + "&" +
                "oauth_timestamp=" + timeStamp + "&" +
                "oauth_token=" + oauthD["tokenValue"] + "&" +
                "oauth_version=1.0");

        var key = HttpUtility.UrlEncode(oauthD["consumerSecret"] + "&" + oauthD["tokenSecret"]);

        var signatureEncoding = new ASCIIEncoding();
        var keyBytes = signatureEncoding.GetBytes(key);
        var signatureBaseBytes = signatureEncoding.GetBytes(signatureBaseString);
        string signatureString;// = HMACSHA1(key, signatureBaseString);
        using (var hmacsha1 = new HMACSHA1(keyBytes))
        {
            var hashBytes = hmacsha1.ComputeHash(signatureBaseBytes);
            signatureString = Convert.ToBase64String(hashBytes);
        }

        signatureString = HttpUtility.UrlEncode(signatureString);


        string SimpleQuote(string s) => '"' + s + '"';
        string header =             
            "oAuth oauth_consumer_key=" + SimpleQuote(oauthD["consumerKey"]) + "," +
            "oauth_nonce=" + SimpleQuote(nonce) + "," +
            "oauth_signature_method=" + SimpleQuote("HMAC-SHA1") + "," +
            "oauth_timestamp=" + SimpleQuote(timeStamp) + "," +
            "oauth_token=" + SimpleQuote(oauthD["tokenValue"]) + "," +
            "oauth_version=" + SimpleQuote("1.0") + "," +
            "oauth_signature= " + SimpleQuote(signatureString);

        return header;
    }

阅读网络和 magento 文档,我找到了很多可能的答案,但我没有设法让 api 以 200 ok 响应。我尝试从 url 中剥离端口,将签名字符串中的参数顺序更改为按字母顺序排列,尝试使用“&”符号进行 urlencoding,并将“&”留在 urlencode 之外。顺便说一句,直接从邮递员调用 api 效果很好。所以我认为我制作签名的方式有问题。任何帮助,将不胜感激。

提前致谢!

标签: c#magento

解决方案


错误出现在我准备散列的字符串和键的过程中。我发现了不同的人提供的各种步骤,最后我把它弄混了,给我带来了问题。最后,我遇到了这个网站,它让事情变得更加清晰,并帮助我结束了我的痛苦。在我的情况下,any1 的最终工作代码是:

private string GetHeader(string method, string cKey, string cSecret, string tSecret, string tValue, string apiUrl)
{
    Dictionary<string, string> oauthD = new Dictionary<string, string>();
    oauthD.Add("consumerKey", cKey);
    oauthD.Add("consumerSecret", cSecret);
    oauthD.Add("tokenSecret", tSecret);
    oauthD.Add("tokenValue", tValue);
    oauthD.Add("url", apiUrl);

    var timeStamp =  ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
    var nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(timeStamp));


    string[] lexOrdering = new string[6];

    lexOrdering[0] = UpperCaseUrlEncode("oauth_consumer_key=" + oauthD["consumerKey"]);
    lexOrdering[1] = UpperCaseUrlEncode("oauth_version=1.0");
    lexOrdering[2] = UpperCaseUrlEncode("oauth_timestamp=" + timeStamp);
    lexOrdering[3] = UpperCaseUrlEncode("oauth_signature_method=HMAC-SHA1");
    lexOrdering[4] = UpperCaseUrlEncode("oauth_token=" + oauthD["tokenValue"]);
    lexOrdering[5] = UpperCaseUrlEncode("oauth_nonce=" + UpperCaseUrlEncode(nonce));

    string orderedString = LexicographicalOrder(lexOrdering);
    var key1 = Encoding.Default.GetBytes(oauthD["consumerSecret"]);
    var key2 = Encoding.Default.GetBytes(oauthD["tokenSecret"]);
    var key = UpperCaseUrlEncode(Encoding.UTF8.GetString(key1)) + "&" + UpperCaseUrlEncode(Encoding.UTF8.GetString(key2));

    string signatureString = Hash(key, method.ToUpper() + "&" + UpperCaseUrlEncode(oauthD["url"]) + "&" + orderedString);

    signatureString = UpperCaseUrlEncode(signatureString);     
    string header =
        "OAuth oauth_consumer_key=\"" + oauthD["consumerKey"] + "\"," +
        "oauth_token=\"" + oauthD["tokenValue"] + "\"," +
        "oauth_signature_method=\"HMAC-SHA1\"," +
        "oauth_timestamp=\"" + timeStamp + "\"," +
        "oauth_nonce=\"" + UpperCaseUrlEncode(nonce) + "\"," +
        "oauth_version=\"1.0\"," +
        "oauth_signature=\"" + signatureString + "\"";

    return header;
}

private string Hash(string Key, string Data)
{
    Byte[] secretBytes = UTF8Encoding.UTF8.GetBytes(Key);
    HMACSHA1 hmac = new HMACSHA1(secretBytes);

    Byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(Data);
    Byte[] calcHash = hmac.ComputeHash(dataBytes);
    String calcHashString = Convert.ToBase64String(calcHash);
    return calcHashString;
}

private string UpperCaseUrlEncode(string s)
{
    char[] temp = HttpUtility.UrlEncode(s).ToCharArray();
    for (int i = 0; i < temp.Length - 2; i++)
    {
        if (temp[i] == '%')
        {
            temp[i + 1] = char.ToUpper(temp[i + 1]);
            temp[i + 2] = char.ToUpper(temp[i + 2]);
        }
    }
    return new string(temp).Normalize();
}

推荐阅读