c# - 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 效果很好。所以我认为我制作签名的方式有问题。任何帮助,将不胜感激。
提前致谢!
解决方案
错误出现在我准备散列的字符串和键的过程中。我发现了不同的人提供的各种步骤,最后我把它弄混了,给我带来了问题。最后,我遇到了这个网站,它让事情变得更加清晰,并帮助我结束了我的痛苦。在我的情况下,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();
}