2014-09-10 15 views
4

我正在寫一個C#方法來爲Twitter生成一個身份驗證標頭。我試圖通過這個API搜索twitter:https://api.twitter.com/1.1/search/tweets.json使用C#爲搜索創建Twitter授權標頭

這裏的URL我打電話:

https://api.twitter.com/1.1/search/tweets.json?q=%23countryman+OR+%23johncooperworks+OR+%40mini%26since_id%3d24012619984051000%26max_id%3d250126199840518145%26result_type%3dmixed%26count%3d4 

這裏是我的方法:

private string GetTwitterAuthHeader() 
{ 
    const string oauthConsumerKey = ""; 
    const string oauthConsumerSecret = ""; 
    const string oauthToken = ""; 
    const string oauthTokenSecret = ""; 
    const string oauthVersion = "1.0"; 
    const string oauthSignatureMethod = "HMAC-SHA1"; 

    var oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture))); 
    var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); 
    var oauthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture); 

    const string resourceUrl = "https://api.twitter.com/1.1/search/tweets.json"; 
    const string baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" + 
           "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}"; 

    var baseString = string.Format(baseFormat, 
           oauthConsumerKey, 
           oauthNonce, 
           oauthSignatureMethod, 
           oauthTimestamp, 
           oauthToken, 
           oauthVersion 
           ); 

    baseString = string.Concat("GET&", Uri.EscapeDataString(resourceUrl), "&", Uri.EscapeDataString(baseString)); 

    var compositeKey = string.Concat(Uri.EscapeDataString(oauthConsumerSecret), 
          "&", Uri.EscapeDataString(oauthTokenSecret)); 

    string oauthSignature; 
    using (var hasher = new HMACSHA1(Encoding.ASCII.GetBytes(compositeKey))) 
    { 
     oauthSignature = Convert.ToBase64String(
      hasher.ComputeHash(Encoding.ASCII.GetBytes(baseString))); 
    } 

    const string headerFormat = "OAuth oauth_consumer_key=\"{0}\", " + 
           "oauth_nonce=\"{1}\", " + 
           "oauth_signature=\"{2}\", " + 
           "oauth_signature_method=\"{3}\", " + 
           "oauth_timestamp=\"{4}\", " + 
           "oauth_token=\"{5}\", " + 
           "oauth_version=\"{6}\""; 

    var authHeader = string.Format(headerFormat, 
          Uri.EscapeDataString(oauthConsumerKey), 
          Uri.EscapeDataString(oauthNonce), 
          Uri.EscapeDataString(oauthSignature), 
          Uri.EscapeDataString(oauthSignatureMethod), 
          Uri.EscapeDataString(oauthTimestamp), 
          Uri.EscapeDataString(oauthToken), 
          Uri.EscapeDataString(oauthVersion) 
        ); 

    return authHeader; 
} 

我得到的錯誤是:

{ 
    "errors": [ 
     { 
      "message": "Bad Authentication data", 
      "code": 215 
     } 
    ] 
} 

任何指針?

我是否需要考慮生成auth頭部時的實際搜索查詢? (例如,我附加到搜索API的值)?

我發現很難調試。

謝謝!

編輯:

意見的基礎上

,這裏是一個更新:

var resourceUrl = "https://api.twitter.com/1.1/search/tweets.json"; 
const string baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" + 
          "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&q={6}"; 

var baseString = string.Format(baseFormat, 
          oauthConsumerKey, 
          oauthNonce, 
          oauthSignatureMethod, 
          oauthTimestamp, 
          oauthToken, 
          oauthVersion, 
          query 
          ); 

baseString = string.Concat("GET&", 
    Uri.EscapeDataString(resourceUrl), "&", 
    Uri.EscapeDataString(baseString)); 

看了https://dev.twitter.com/oauth/overview/creating-signatures接近,這似乎是正確的。儘管如此,我也遇到了同樣的錯誤。

+0

是的,請求參數需要包含在OAuth 1中籤名的字符串中。 – smarx 2014-09-10 17:57:14

+1

這裏是Twitter的文檔(但我相信它只是遵循OAuth 1.0a規範):https://dev.twitter.com/oauth /概述/創造的簽名。 – smarx 2014-09-10 17:58:11

+1

這個工程:https://github.com/Twitterizer/Twitterizer/tree/develop/Twitterizer2/OAuth – 2014-09-10 17:58:41

回答

2

將您的「查詢」包裝在Uri.EscapeDataString()中。

上面的確切代碼適用於我,如果我這樣做。也就是說,你真的應該逃避所有這些參數鍵和值,我有點難以理解,爲什麼nonce工作不逃脫,因爲它是base64編碼。但是也許我在測試中很幸運,並且從來沒有在其中發生過一次亂糟糟的事情。

+2

這就是它!天哪,這些小東西讓我們感到驚奇,感謝您的幫助。如果任何人有興趣,這裏的結果:https://github.com/wadewegner/TwitterOAuthRESTAPI/tree/master/src – Wade 2014-09-12 19:53:10

+0

@等等,2年後,我有一個關於你的TwitterOAut的問題hRESTAPI代碼。在'GetHeader(...)'函數中,爲什麼要做'UriBuilder'?你不能只使用你傳入的'uri'變量嗎? – Daevin 2016-10-13 20:49:09

+0

不是韋德,但他這樣做是爲了消除查詢字符串,因爲查詢字符串不會進入字符串到符號的資源URL部分。 – smarx 2016-10-14 01:25:19

3

所以,我寫了一個這樣的類。讓我們看看我能把它分解得多好。我們從整個課程開始。

public class TwitterAuthTool : IDisposable { 
    private const string BASE_AUTH_URL = "https://api.twitter.com/oauth2/token"; 
    private const string BASE_SEARCH_URL = "https://api.twitter.com/1.1/search/tweets.json"; 
    private const string BASE_INVALIDATE_URL = "https://api.twitter.com/oauth2/invalidate_token"; 

    private AccessToken Credentials; 
    private string BearerTokenCredentials; 

    public TwitterAuthTool(string p_ConsumerKey, string p_ConsumerSecret) { 
     BearerTokenCredentials = 
      Convert.ToBase64String(
       Encoding.ASCII.GetBytes(
        string.Format("{0}:{1}", 
         Uri.EscapeUriString(p_ConsumerKey), 
         Uri.EscapeUriString(p_ConsumerSecret)))); 

     HttpWebRequest _Request = HttpWebRequest.Create(BASE_AUTH_URL) as HttpWebRequest; 
     _Request.KeepAlive = false; 
     _Request.Method = "POST"; 
     _Request.Headers.Add("Authorization", string.Format("Basic {0}", BearerTokenCredentials)); 
     _Request.ContentType = "application/x-www-form-urlencoded;charset=UTF-8"; 

     byte[] _Content = Encoding.ASCII.GetBytes("grant_type=client_credentials"); 
     using(Stream _Stream = _Request.GetRequestStream()) 
      _Stream.Write(_Content, 0, _Content.Length); 

     HttpWebResponse _Response = _Request.GetResponse() as HttpWebResponse; 

     DataContractJsonSerializer _AccessTokenJsonSerializer = new DataContractJsonSerializer(typeof(AccessToken)); 
     Credentials = (AccessToken)_AccessTokenJsonSerializer.ReadObject(_Response.GetResponseStream()); 
    } 

    public List<Tweet> GetLatest(string p_Query, int p_Count = 100) { 
     TwitterResults _TwitterResults; 
     List<Tweet> _ReturnValue = new List<Tweet>(); 
     DataContractJsonSerializer _JsonSerializer = new DataContractJsonSerializer(typeof(TwitterResults)); 

     HttpWebRequest _Request = WebRequest.Create(string.Format("{0}?q={1}&result_type=recent&count={2}", BASE_SEARCH_URL, p_Query, p_Count)) as HttpWebRequest; 
     _Request.Headers.Add("Authorization", string.Format("Bearer {0}", Credentials.access_token)); 
     _Request.KeepAlive = false; 
     _Request.Method = "GET"; 

     HttpWebResponse _Response = _Request.GetResponse() as HttpWebResponse; 
     _TwitterResults = (TwitterResults)_JsonSerializer.ReadObject(_Response.GetResponseStream()); 
     _ReturnValue.AddRange(_TwitterResults.statuses); 

     while(!string.IsNullOrWhiteSpace(_TwitterResults.search_metadata.next_results)) { 
      _Request = WebRequest.Create(string.Format("{0}{1}", BASE_SEARCH_URL, _TwitterResults.search_metadata.next_results)) as HttpWebRequest; 
      _Request.Headers.Add("Authorization", string.Format("Bearer {0}", Credentials.access_token)); 
      _Request.KeepAlive = false; 
      _Request.Method = "GET"; 

      _Response = _Request.GetResponse() as HttpWebResponse; 
      _TwitterResults = (TwitterResults)_JsonSerializer.ReadObject(_Response.GetResponseStream()); 
      _ReturnValue.AddRange(_TwitterResults.statuses); 
     } 

     return _ReturnValue; 
    } 

    public List<Tweet> GetLatestSince(string p_Query, long p_SinceId, int p_Count = 100) { 
     TwitterResults _TwitterResults; 
     List<Tweet> _ReturnValue = new List<Tweet>(); 
     DataContractJsonSerializer _JsonSerializer = new DataContractJsonSerializer(typeof(TwitterResults)); 

     HttpWebRequest _Request = WebRequest.Create(string.Format("{0}?q={1}&result_type=recent&count={2}&since_id={3}", BASE_SEARCH_URL, p_Query, p_Count, p_SinceId)) as HttpWebRequest; 
     _Request.Headers.Add("Authorization", string.Format("Bearer {0}", Credentials.access_token)); 
     _Request.KeepAlive = false; 
     _Request.Method = "GET"; 

     HttpWebResponse _Response = _Request.GetResponse() as HttpWebResponse; 
     _TwitterResults = (TwitterResults)_JsonSerializer.ReadObject(_Response.GetResponseStream()); 
     _ReturnValue.AddRange(_TwitterResults.statuses); 

     while(!string.IsNullOrWhiteSpace(_TwitterResults.search_metadata.next_results)) { 
      _Request = WebRequest.Create(string.Format("{0}{1}", BASE_SEARCH_URL, _TwitterResults.search_metadata.next_results)) as HttpWebRequest; 
      _Request.Headers.Add("Authorization", string.Format("Bearer {0}", Credentials.access_token)); 
      _Request.KeepAlive = false; 
      _Request.Method = "GET"; 

      _Response = _Request.GetResponse() as HttpWebResponse; 
      _TwitterResults = (TwitterResults)_JsonSerializer.ReadObject(_Response.GetResponseStream()); 
      _ReturnValue.AddRange(_TwitterResults.statuses); 
     } 

     return _ReturnValue; 
    } 

    public void Dispose() { 
     HttpWebRequest _Request = WebRequest.Create(BASE_INVALIDATE_URL) as HttpWebRequest; 
     _Request.KeepAlive = false; 
     _Request.Method = "POST"; 
     _Request.Headers.Add("Authorization", string.Format("Basic {0}", BearerTokenCredentials)); 
     _Request.ContentType = "application/x-www-form-urlencoded"; 

     byte[] _Content = Encoding.ASCII.GetBytes(string.Format("access_token={0}", Credentials.access_token)); 
     using(Stream _Stream = _Request.GetRequestStream()) 
      _Stream.Write(_Content, 0, _Content.Length); 

     try { 
      _Request.GetResponse(); 
     } catch { 
      // The bearer token will time out if this fails. 
     } 
    } 
} 

Twitter使用了OAuth2-ish。你必須先登錄才能做任何其他事情。這是上面類的構造函數的作用。當你這樣做時,你會得到一個訪問令牌。這是一個簡單的小JSON對象,可以輕鬆地反序列化到下面的小類中。

[DataContract] 
public class AccessToken { 
    [DataMember] 
    public string token_type; 

    [DataMember] 
    public string access_token; 
} 

一旦您擁有了正確的憑據,就可以使用THOSE來訪問事物。不幸的是,你從查詢中得到的JSON對象是巨大的。我不得不創建17個不同的類來反序列化它的全部內容。我會考慮把這個庫放在github上。

正如您在Dispose中看到的,我不在乎auth令牌是否失敗。 8小時後,他們給你的憑證會超時。

+1

我把整個項目放在github上:https://github.com/freestylecoder/TwitterAuthTool – 2014-09-10 21:06:13

+0

謝謝!不過,我真的想用三腳認證流程來實現這個目標。 – Wade 2014-09-12 19:53:49