我试图从Twitter OAuth获取请求令牌,但我不断获得401 Unauthorized
,但我不知道为什么。我试图尽可能地遵循签名创建并且与400 Bad Request
进行了一段时间的努力,但最终破解了代码以获得401的有效请求。
我觉得我错过了一些非常简单的东西,但我无法找到答案。
以下是我使用的代码:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.twitter.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
string oauth_callback = Uri.EscapeUriString("oob");
string oauth_signature_method = "HMAC-SHA1";
string oauth_timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();
string oauth_consumer_key = OAUTH_KEY;
string oauth_version = "1.0";
// Generate signature
string baseString = "POST&";
baseString += Uri.EscapeDataString(client.BaseAddress + "oauth/request_token") + "&";
// Each oauth value sorted alphabetically
baseString += Uri.EscapeDataString("oauth_callback=" + oauth_callback + "&");
baseString += Uri.EscapeDataString("oauth_consumer_key=" + oauth_consumer_key + "&");
baseString += Uri.EscapeDataString("oauth_nonce=" + oauth_nonce + "&");
baseString += Uri.EscapeDataString("oauth_signature_method=" + oauth_signature_method + "&");
baseString += Uri.EscapeDataString("oauth_timestamp=" + oauth_timestamp + "&");
baseString += Uri.EscapeDataString("oauth_version=" + oauth_version + "&");
string signingKey = Uri.EscapeDataString(OAUTH_SECRET) + "&";
HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
string oauth_signature = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));
client.DefaultRequestHeaders.Add("Authorization", "OAuth " +
"oauth_nonce=\"" + oauth_nonce + "\"," +
"oauth_callback=\"" + oauth_callback + "\"," +
"oauth_signature_method=\"" + oauth_signature_method + "\"," +
"oauth_timestamp=\"" + oauth_timestamp + "\"," +
"oauth_consumer_key=\"" + oauth_consumer_key + "\"," +
"oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) + "\"," +
"oauth_version=\"" + oauth_version + "\""
);
HttpResponseMessage response = await client.PostAsJsonAsync("oauth/request_token", "");
var responseString = await response.Content.ReadAsStringAsync();
}
我使用的是.Net 4.5,因此Uri.EscapeDataString()
应该提供正确的百分比编码字符串。
修改
我注意到oauth_nonce
值有时包含非字母数字字符,因此我将变量计算更改为以下内容:
string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
string oauth_nonce = new string(nonce.Where(c => Char.IsLetter(c) || Char.IsDigit(c)).ToArray());
遗憾的是,这并没有帮助。
我还为Uri.EscapeUriData()
标题中的所有值尝试了Authorization
,并在每个逗号后添加了一个空格(正如Twitter上的文档所说的那样),但是没有&也可以帮助。
答案 0 :(得分:2)
我从零开始尝试了一种稍微不同的方法,但它应该产生相同的结果。但是,它没有,而是按预期工作!
如果有人能够指出这个工作代码与原始问题中的代码之间的实际区别,我会非常感激,因为在我看来它们会产生相同的结果。
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.twitter.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
TimeSpan timestamp = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
string nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
Dictionary<string, string> oauth = new Dictionary<string, string>
{
["oauth_nonce"] = new string(nonce.Where(c => char.IsLetter(c) || char.IsDigit(c)).ToArray()),
["oauth_callback"] = "http://my.callback.url",
["oauth_signature_method"] = "HMAC-SHA1",
["oauth_timestamp"] = Convert.ToInt64(timestamp.TotalSeconds).ToString(),
["oauth_consumer_key"] = OAUTH_KEY,
["oauth_version"] = "1.0"
};
string[] parameterCollectionValues = oauth.Select(parameter =>
Uri.EscapeDataString(parameter.Key) + "=" +
Uri.EscapeDataString(parameter.Value))
.OrderBy(kv => kv)
.ToArray();
string parameterCollection = string.Join("&", parameterCollectionValues);
string baseString = "POST";
baseString += "&";
baseString += Uri.EscapeDataString(client.BaseAddress + "oauth/request_token");
baseString += "&";
baseString += Uri.EscapeDataString(parameterCollection);
string signingKey = Uri.EscapeDataString(OAUTH_SECRET);
signingKey += "&";
HMACSHA1 hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
oauth["oauth_signature"] = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(baseString)));
string headerString = "OAuth ";
string[] headerStringValues = oauth.Select(parameter =>
Uri.EscapeDataString(parameter.Key) + "=" + "\"" +
Uri.EscapeDataString(parameter.Value) + "\"")
.ToArray();
headerString += string.Join(", ", headerStringValues);
client.DefaultRequestHeaders.Add("Authorization", headerString);
HttpResponseMessage response = await client.PostAsJsonAsync("oauth/request_token", "");
var responseString = await response.Content.ReadAsStringAsync();
}
答案 1 :(得分:0)
谢谢@GTHvidsten的例子。
通过深入研究你的第二个例子,我发现了我的代码所遇到的问题,这可能与原始示例无效的原因相同。
对构成签名基本字符串的各个请求参数键/值对进行编码是不够的,但显然您还必须对整个连接键/值请求参数字符串进行编码,从而对“=”符号进行编码。在第二个示例中,这行代码就是这样:baseString += Uri.EscapeDataString(parameterCollection);
在您的原始示例中,没有代码对请求参数进行编码。