由于无效的 OAuth 签名而涉及查询参数时,GET 失败并显示 401(未经授权) [英] GET fails with 401 (Unauthorized) when query parameter is involved due to invalid OAuth signature
问题描述
我正在查询使用 OAuth1 的 API.我可以使用 RestSharp 成功进行查询:
I'm querying an API that uses OAuth1. I can make a query successfully using RestSharp:
var client = new RestClient("https://api.thirdparty.com/1/");
client.Authenticator =
OAuth1Authenticator.ForAccessToken(appKey, appSecret, token, tokenSecret);
var request = new RestRequest("projects/12345/documents", Method.GET);
request.AddParameter("recursive", "true");
var response = client.Execute(request);
不幸的是,我无法在实际项目中使用 RestSharp(我只是在此处使用它来验证 API 调用是否有效),因此我尝试仅使用普通的 .NET 来让 OAuth 正常工作.
Unfortunately I'm unable to use RestSharp in the actual project (I'm just using it here to verify that the API call works) so I've tried to get OAuth working using just plain .NET.
我让它处理不使用查询参数的请求,所以直接GET
到https://api.thirdparty.com/1/projects/12345/documents代码> 工作得很好.
I have it working for requests that do not utilise query parameters, so a straightforward GET
to https://api.thirdparty.com/1/projects/12345/documents
works just fine.
只要我尝试使用诸如 https://api.../documents?recursive=true
之类的查询参数,如 RestSharp 示例中所示,我就会得到 401 Unauthorized
错误,因为我认为我的 OAuth 签名无效.
As soon as I try to use a query parameter such as https://api.../documents?recursive=true
as shown in the RestSharp sample I get a 401 Unauthorized
error as I think my OAuth signature is not valid.
这是我生成 OAuth 签名和请求的方式.有人能告诉我在涉及查询参数时如何生成有效签名吗?
Here is how I'm generating the OAuth signature and request. Can anybody tell me how to generate a valid signature when query parameters are involved?
static string appKey = @"e8899de00";
static string appSecret = @"bffe04d6";
static string token = @"6e85a21a";
static string tokenSecret = @"e137269f";
static string baseUrl = "https://api.thirdparty.com/1/";
static HttpClient httpclient;
static HMACSHA1 hasher;
static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
static void Main(string[] args)
{
// SETUP HTTPCLIENT
httpclient = new HttpClient();
httpclient.BaseAddress = new Uri(baseUrl);
httpclient.DefaultRequestHeaders.Accept.Clear();
httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// SETUP THE HASH MESSAGE AUTHENTICATION CODE (HMAC) WITH SHA1
hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(string.Format("{0}&{1}", appSecret, tokenSecret)));
// WORKS IF QUERY PARAMETER IS MISSED OFF THE END, FAILS WITH 401 IF INCLUDED
var document =
Request(HttpMethod.Get, "projects/12345/documents?recursive=true");
}
static string Request(HttpMethod method, string url)
{
// CREATE A TIMESTAMP OF CURRENT EPOCH TIME
var timestamp = (int)((DateTime.UtcNow - epoch).TotalSeconds);
// DICTIONARY WILL HOLD THE KEY/PAIR VALUES FOR OAUTH
var oauth = new Dictionary<string, string>();
oauth.Add("oauth_consumer_key", appKey);
oauth.Add("oauth_signature_method", "HMAC-SHA1");
oauth.Add("oauth_timestamp", timestamp.ToString());
oauth.Add("oauth_nonce", "nonce");
oauth.Add("oauth_token", token);
oauth.Add("oauth_version", "1.0");
// GENERATE OAUTH SIGNATURE
oauth.Add("oauth_signature", GenerateSignature(method.ToString(), string.Concat(baseUrl, url), oauth));
// GENERATE THE REQUEST
using (var request = new HttpRequestMessage())
{
// URL AND METHOD
request.RequestUri = new Uri(string.Concat(baseUrl, url));
request.Method = method;
// GENERATE AUTHORIZATION FOR THIS REQUEST
request.Headers.Add("Authorization", GenerateOAuthHeader(oauth));
// MAKE REQUEST
var response = httpclient.SendAsync(request).Result;
// ENSURE IT WORKED
response.EnsureSuccessStatusCode(); // THROWS 401 UNAUTHORIZED
// RETURN CONTENT
return response.Content.ReadAsStringAsync().Result;
};
}
static string GenerateSignature(string verb, string url, Dictionary<string, string> data)
{
var signaturestring = string.Join(
"&",
data
.OrderBy(s => s.Key)
.Select(kvp => string.Format(
"{0}={1}",
Uri.EscapeDataString(kvp.Key),
Uri.EscapeDataString(kvp.Value))
)
);
var signaturedata = string.Format(
"{0}&{1}&{2}",
verb,
Uri.EscapeDataString(url),
Uri.EscapeDataString(signaturestring.ToString())
);
return Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(signaturedata.ToString())));
}
static string GenerateOAuthHeader(Dictionary<string, string> data)
{
return "OAuth " + string.Join(
", ",
data
.Select(kvp => string.Format("{0}=\"{1}\"", Uri.EscapeDataString(kvp.Key), Uri.EscapeDataString(kvp.Value)))
.OrderBy(s => s)
);
}
推荐答案
在 oauth 1 中,查询字符串参数(以及 POST 参数,以防您使用 x-www-form-urlencoded
进行 POST,所以它们就像正文中的a=1&b=2")应该包含在键值对列表中,然后您对其进行排序和签名.因此,要获得正确的签名,您必须:
In oauth 1, query string parameters (and also POST parameters in case you POST with x-www-form-urlencoded
, so they are like "a=1&b=2" in body) should be included in list of key-value pairs which you then sort and sign. So to get correct signature you have to:
- 将所有查询字符串(和 POST,如上)参数提取为键值对
- 从 url 中删除查询字符串
- 像您现在所做的那样对所有内容进行签名(没有查询字符串的 url,以及所有密钥对,包括上面提取的和特定于 oauth 的)
这篇关于由于无效的 OAuth 签名而涉及查询参数时,GET 失败并显示 401(未经授权)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!