由于无效的 OAuth 签名而涉及查询参数时,GET 失败并显示 401(未经授权) [英] GET fails with 401 (Unauthorized) when query parameter is involved due to invalid OAuth signature

查看:51
本文介绍了由于无效的 OAuth 签名而涉及查询参数时,GET 失败并显示 401(未经授权)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在查询使用 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.

我让它处理不使用查询参数的请求,所以直接GEThttps://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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆