2013-04-23 4 views
1

Oauth가 Twitter 스트리밍 API에서 작동하도록하려고하지만 401 Unauthorized 오류가 발생합니다. Oauth 인증을위한 코드를 제공하는 블로그를 발견했지만이를 컴파일하기 위해 수정해야했습니다. 내가 올바른 사용하고Twitter 해시 태그를 가져 오기위한 스트리밍 API 및 Oauth

using OAuth; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 
using System.Web; 

namespace LiveTwitter 
{ 
    public interface IRequestAuthorizer 
    { 
     void Authorize(HttpWebRequest request, string postData); 
    } 

    public class OAuthRequestAuthorizer : OAuthBase, IRequestAuthorizer 
    { 
     private readonly string _twitterConsumerKey; 
     private readonly string _twitterConsumerSecret; 
     private readonly string _twitterAccessToken; 
     private readonly string _twitterAccessTokenSecret; 

     public OAuthRequestAuthorizer(string twitterConsumerKey, string twitterConsumerSecret, string twitterAccessToken, string twitterAccessTokenSecret) 
     { 
      _twitterConsumerKey = twitterConsumerKey; 
      _twitterConsumerSecret = twitterConsumerSecret; 
      _twitterAccessToken = twitterAccessToken; 
      _twitterAccessTokenSecret = twitterAccessTokenSecret; 
     } 

     public override string GenerateNonce() 
     { 
      return new Nonce().Value; 
     } 

     private string BuildHeader(HttpWebRequest request, Uri uri) 
     { 
      var nonce = GenerateNonce(); 
      var timeStamp = GenerateTimeStamp(); 

      string normalizedUrl; 
      string normalizedRequestParameters; 

      var httpMethod = request.Method; 
      var signature = GenerateSignature(uri, _twitterConsumerKey, _twitterConsumerSecret, _twitterAccessToken, _twitterAccessTokenSecret, 
               httpMethod, timeStamp, nonce, out normalizedUrl, 
               out normalizedRequestParameters); 

      // https://dev.twitter.com/docs/auth/authorizing-request 
      return 
       string.Format(
        "OAuth oauth_consumer_key=\"{0}\", " + 
        "oauth_nonce=\"{1}\", " + 
        "oauth_signature=\"{2}\", " + 
        "oauth_signature_method=\"HMAC-SHA1\", " + 
        "oauth_timestamp=\"{3}\", " + 
        "oauth_token=\"{4}\", " + 
        "oauth_version=\"1.0\"", 

        UrlEncode(_twitterConsumerKey), 
        UrlEncode(_twitterConsumerKey), 
        UrlEncode(nonce), 
        UrlEncode(signature), 
        UrlEncode(timeStamp), 
        UrlEncode(_twitterAccessToken)); 
     } 

     public void Authorize(HttpWebRequest request, string postData) 
     { 
      //NOTE: It's a must to collect all param either in the header/querystring or post body 
      var baseUri = string.IsNullOrEmpty(postData) || request.Method.ToUpper() == "GET" 
         ? request.RequestUri 
         : new Uri(string.Format("{0}?{1}", request.RequestUri.AbsoluteUri, postData)); 

      request.Headers["Authorization"] = BuildHeader(request, baseUri); 
     } 
    } 

    public class Nonce 
    { 
     public string Value { get; set; } 

     public Nonce() 
     { 
      Value = Guid.NewGuid().ToString(); 
     } 
    } 
} 

: 여기

 HttpWebRequest webRequest = null; 
     HttpWebResponse webResponse = null; 
     StreamReader responseStream = null; 

     String twitterUser = Properties.Settings.Default.TwitterAccount; 
     String twitterPassword = Properties.Settings.Default.TwitterPassword; 
     String twitterHashtags = Properties.Settings.Default.TwitterHashtags; 

     String twitterConsumerKey = Properties.Settings.Default.TwitterConsumerKey; 
     String twitterConsumerSecret = Properties.Settings.Default.TwitterConsumerSecret; 
     String twitterAccessToken = Properties.Settings.Default.TwitterAccessTokens; 
     String twitterAccessTokenSecret = Properties.Settings.Default.TwitterAccessTokenSecret; 

     Int32 wait = 250; 

     while (true) //Auto retry on error! 
     { 
      try 
      { 
       var postData = "track=" + HttpUtility.UrlEncode(twitterHashtags); 
       var requestContentBuffer = Encoding.UTF8.GetBytes(postData); 

       webRequest = (HttpWebRequest)WebRequest.Create(new Uri("https://stream.twitter.com/1.1/statuses/filter.json")); 
       webRequest.Method = "POST"; 
       webRequest.ContentLength = requestContentBuffer.Length; 
       webRequest.ContentType = "application/x-www-form-urlencoded"; 

       webRequest.UserAgent = "OAuthTwitterStream"; 
       webRequest.Headers["Accept-Encoding"] = "deflate, gzip"; 
       webRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 

       OAuthRequestAuthorizer _requestAuthorizer = new OAuthRequestAuthorizer(twitterConsumerKey, twitterConsumerSecret, twitterAccessToken, twitterAccessTokenSecret); 

       _requestAuthorizer.Authorize(webRequest, postData); 

       using (var dataStream = webRequest.GetRequestStream()) 
       { 
        dataStream.Write(requestContentBuffer, 0, requestContentBuffer.Length); 
       } 

       webResponse = (HttpWebResponse)webRequest.GetResponse(); 
       responseStream = new StreamReader(webResponse.GetResponseStream()); 

       while (true) 
       { 
        string jsonText = responseStream.ReadLine(); 

        dynamic o = JsonConvert.DeserializeObject(jsonText); 

        if (o.disconnect == null) //Sometimes you get a disconnect response, we don't want our clients to see this. 
        { 
         //do something with tweet object 
        } 
       } 

      } 
      catch (WebException ee) 
      { 
       if (ee.Status == WebExceptionStatus.ProtocolError) 
       { 
        if (wait < 10000) 
         wait = 10000; 
        else 
        { 
         if (wait < 240000) 
          wait = wait * 2; 
        } 
       } 

       Console.Error.WriteLine(ee.Message); 

      } 
      catch (Exception ee) 
      { 
       Console.Error.WriteLine(ee.Message); 
      } 
      finally 
      { 
       if (webRequest != null) 
       { 
        webRequest.Abort(); 
       } 
       if (responseStream != null) 
       { 
        responseStream.Close(); 
        responseStream = null; 
       } 

       if (webResponse != null) 
       { 
        webResponse.Close(); 
        webResponse = null; 
       } 
       Console.WriteLine("Waiting: " + wait); 
       Thread.Sleep(wait); 
      } 
     } 

그리고의 Oauth 클래스입니다 :

using System; 
using System.Security.Cryptography; 
using System.Collections.Generic; 
using System.Text; 
using System.Web; 

namespace OAuth 
{ 
    public class OAuthBase 
    { 

     /// <summary> 
     /// Provides a predefined set of algorithms that are supported officially by the protocol 
     /// </summary> 
     public enum SignatureTypes 
     { 
      HMACSHA1, 
      PLAINTEXT, 
      RSASHA1 
     } 

     /// <summary> 
     /// Provides an internal structure to sort the query parameter 
     /// </summary> 
     protected class QueryParameter 
     { 
      private string name = null; 
      private string value = null; 

      public QueryParameter(string name, string value) 
      { 
       this.name = name; 
       this.value = value; 
      } 

      public string Name 
      { 
       get { return name; } 
      } 

      public string Value 
      { 
       get { return value; } 
      } 
     } 

     /// <summary> 
     /// Comparer class used to perform the sorting of the query parameters 
     /// </summary> 
     protected class QueryParameterComparer : IComparer<QueryParameter> 
     { 

      #region IComparer<QueryParameter> Members 

      public int Compare(QueryParameter x, QueryParameter y) 
      { 
       if (x.Name == y.Name) 
       { 
        return string.Compare(x.Value, y.Value); 
       } 
       else 
       { 
        return string.Compare(x.Name, y.Name); 
       } 
      } 

      #endregion 
     } 

     protected const string OAuthVersion = "1.0"; 
     protected const string OAuthParameterPrefix = "oauth_"; 

     // 
     // List of know and used oauth parameters' names 
     //   
     protected const string OAuthConsumerKeyKey = "oauth_consumer_key"; 
     protected const string OAuthCallbackKey = "oauth_callback"; 
     protected const string OAuthVersionKey = "oauth_version"; 
     protected const string OAuthSignatureMethodKey = "oauth_signature_method"; 
     protected const string OAuthSignatureKey = "oauth_signature"; 
     protected const string OAuthTimestampKey = "oauth_timestamp"; 
     protected const string OAuthNonceKey = "oauth_nonce"; 
     protected const string OAuthTokenKey = "oauth_token"; 
     protected const string OAuthTokenSecretKey = "oauth_token_secret"; 

     protected const string HMACSHA1SignatureType = "HMAC-SHA1"; 
     protected const string PlainTextSignatureType = "PLAINTEXT"; 
     protected const string RSASHA1SignatureType = "RSA-SHA1"; 

     protected Random random = new Random(); 

     protected string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~"; 

     /// <summary> 
     /// Helper function to compute a hash value 
     /// </summary> 
     /// <param name="hashAlgorithm">The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function</param> 
     /// <param name="data">The data to hash</param> 
     /// <returns>a Base64 string of the hash value</returns> 
     private string ComputeHash(HashAlgorithm hashAlgorithm, string data) 
     { 
      if (hashAlgorithm == null) 
      { 
       throw new ArgumentNullException("hashAlgorithm"); 
      } 

      if (string.IsNullOrEmpty(data)) 
      { 
       throw new ArgumentNullException("data"); 
      } 

      byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data); 
      byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer); 

      return Convert.ToBase64String(hashBytes); 
     } 

     /// <summary> 
     /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") 
     /// </summary> 
     /// <param name="parameters">The query string part of the Url</param> 
     /// <returns>A list of QueryParameter each containing the parameter name and value</returns> 
     private List<QueryParameter> GetQueryParameters(string parameters) 
     { 
      if (parameters.StartsWith("?")) 
      { 
       parameters = parameters.Remove(0, 1); 
      } 

      List<QueryParameter> result = new List<QueryParameter>(); 

      if (!string.IsNullOrEmpty(parameters)) 
      { 
       string[] p = parameters.Split('&'); 
       foreach (string s in p) 
       { 
        if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix)) 
        { 
         if (s.IndexOf('=') > -1) 
         { 
          string[] temp = s.Split('='); 
          result.Add(new QueryParameter(temp[0], temp[1])); 
         } 
         else 
         { 
          result.Add(new QueryParameter(s, string.Empty)); 
         } 
        } 
       } 
      } 

      return result; 
     } 

     /// <summary> 
     /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case. 
     /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth 
     /// </summary> 
     /// <param name="value">The value to Url encode</param> 
     /// <returns>Returns a Url encoded string</returns> 
     protected string UrlEncode(string value) 
     { 
      StringBuilder result = new StringBuilder(); 

      foreach (char symbol in value) 
      { 
       if (unreservedChars.IndexOf(symbol) != -1) 
       { 
        result.Append(symbol); 
       } 
       else 
       { 
        result.Append('%' + String.Format("{0:X2}", (int)symbol)); 
       } 
      } 

      return result.ToString(); 
     } 

     /// <summary> 
     /// Normalizes the request parameters according to the spec 
     /// </summary> 
     /// <param name="parameters">The list of parameters already sorted</param> 
     /// <returns>a string representing the normalized parameters</returns> 
     protected string NormalizeRequestParameters(IList<QueryParameter> parameters) 
     { 
      StringBuilder sb = new StringBuilder(); 
      QueryParameter p = null; 
      for (int i = 0; i < parameters.Count; i++) 
      { 
       p = parameters[i]; 
       sb.AppendFormat("{0}={1}", p.Name, p.Value); 

       if (i < parameters.Count - 1) 
       { 
        sb.Append("&"); 
       } 
      } 

      return sb.ToString(); 
     } 

     /// <summary> 
     /// Generate the signature base that is used to produce the signature 
     /// </summary> 
     /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> 
     /// <param name="consumerKey">The consumer key</param>   
     /// <param name="token">The token, if available. If not available pass null or an empty string</param> 
     /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> 
     /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> 
     /// <param name="signatureType">The signature type. To use the default values use <see cref="OAuthBase.SignatureTypes">OAuthBase.SignatureTypes</see>.</param> 
     /// <returns>The signature base</returns> 
     public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters) 
     { 
      if (token == null) 
      { 
       token = string.Empty; 
      } 

      if (tokenSecret == null) 
      { 
       tokenSecret = string.Empty; 
      } 

      if (string.IsNullOrEmpty(consumerKey)) 
      { 
       throw new ArgumentNullException("consumerKey"); 
      } 

      if (string.IsNullOrEmpty(httpMethod)) 
      { 
       throw new ArgumentNullException("httpMethod"); 
      } 

      if (string.IsNullOrEmpty(signatureType)) 
      { 
       throw new ArgumentNullException("signatureType"); 
      } 

      normalizedUrl = null; 
      normalizedRequestParameters = null; 

      List<QueryParameter> parameters = GetQueryParameters(url.Query); 
      parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion)); 
      parameters.Add(new QueryParameter(OAuthNonceKey, nonce)); 
      parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp)); 
      parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType)); 
      parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); 

      if (!string.IsNullOrEmpty(token)) 
      { 
       parameters.Add(new QueryParameter(OAuthTokenKey, token)); 
      } 

      parameters.Sort(new QueryParameterComparer()); 

      normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host); 
      if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443))) 
      { 
       normalizedUrl += ":" + url.Port; 
      } 
      normalizedUrl += url.AbsolutePath; 
      normalizedRequestParameters = NormalizeRequestParameters(parameters); 

      StringBuilder signatureBase = new StringBuilder(); 
      signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()); 
      signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl)); 
      signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters)); 

      return signatureBase.ToString(); 
     } 

     /// <summary> 
     /// Generate the signature value based on the given signature base and hash algorithm 
     /// </summary> 
     /// <param name="signatureBase">The signature based as produced by the GenerateSignatureBase method or by any other means</param> 
     /// <param name="hash">The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method</param> 
     /// <returns>A base64 string of the hash value</returns> 
     public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash) 
     { 
      return ComputeHash(hash, signatureBase); 
     } 

     /// <summary> 
     /// Generates a signature using the HMAC-SHA1 algorithm 
     /// </summary>  
     /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> 
     /// <param name="consumerKey">The consumer key</param> 
     /// <param name="consumerSecret">The consumer seceret</param> 
     /// <param name="token">The token, if available. If not available pass null or an empty string</param> 
     /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> 
     /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> 
     /// <returns>A base64 string of the hash value</returns> 
     public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters) 
     { 
      return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); 
     } 

     /// <summary> 
     /// Generates a signature using the specified signatureType 
     /// </summary>  
     /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> 
     /// <param name="consumerKey">The consumer key</param> 
     /// <param name="consumerSecret">The consumer seceret</param> 
     /// <param name="token">The token, if available. If not available pass null or an empty string</param> 
     /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> 
     /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> 
     /// <param name="signatureType">The type of signature to use</param> 
     /// <returns>A base64 string of the hash value</returns> 
     public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) 
     { 
      normalizedUrl = null; 
      normalizedRequestParameters = null; 

      switch (signatureType) 
      { 
       case SignatureTypes.PLAINTEXT: 
        return HttpUtility.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret)); 
       case SignatureTypes.HMACSHA1: 
        string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters); 

        HMACSHA1 hmacsha1 = new HMACSHA1(); 
        hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret))); 

        return GenerateSignatureUsingHash(signatureBase, hmacsha1); 
       case SignatureTypes.RSASHA1: 
        throw new NotImplementedException(); 
       default: 
        throw new ArgumentException("Unknown signature type", "signatureType"); 
      } 
     } 

     /// <summary> 
     /// Generate the timestamp for the signature   
     /// </summary> 
     /// <returns></returns> 
     public virtual string GenerateTimeStamp() 
     { 
      // Default implementation of UNIX time of the current UTC time 
      TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 
      return Convert.ToInt64(ts.TotalSeconds).ToString(); 
     } 

     /// <summary> 
     /// Generate a nonce 
     /// </summary> 
     /// <returns></returns> 
     public virtual string GenerateNonce() 
     { 
      // Just a simple implementation of a random number between 123400 and 9999999 
      return random.Next(123400, 9999999).ToString(); 
     } 

    } 
} 

그리고 마지막으로 OAuthRequestAuthorizer 클래스 여기에 지금까지 내 주요 방법이 무엇 API 키 및 응용 프로그램에 올바른 설정이 있다고 생각합니다. 내가 왜 계속 401을 얻는 지 아는가? 또한, 이것을 할 수있는 간단한 방법이 있다면 꼭 알려주십시오!

답변

0

Temboo는 두 통화로를 분해하여 트위터의 OAuth는 과정을 단순화 :

  • InitializeOAuth - 당신이 사용자에게 표시 할 수 있습니다 그들 자신의 트위터 계정에 애플리케이션 액세스 권한을 부여했다 트위터 인증 URL을 반환
  • FinalizeOAuth - 인증 된 액세스에 필요한 액세스 토큰을 Twitter Streaming API에 반환합니다.

브라우저의 Temboo의 Twitter OAuth 지원을 아래 링크에서 시험해보고 앱에서이 동작을 사용하는 데 필요한 소스 코드를 생성 할 수 있습니다. 귀하의 언어가 지원되지 않는 경우 (Temboo는 현재 C# SDK가 없음), REST API cURL 명령을 생성 할 수 있습니다.

https://www.temboo.com/library/Library/Twitter/OAuth/

(전체 공개! 내가 Temboo에서 일하고, 그래서 당신은 어떤 질문이 있으면 알려 주시기)

0

당신은 아마 당신의 버그를 발견하거나하여 문제를 해결하는 또 다른 방법을 찾았 중 지금은 그렇지 않은 경우를 대비하여 401 오류는 OAuthRequestAuthorizer 클래스의 복사 붙여 넣기 오류로 인한 것입니다. BuildHeader 함수의 string.format 메서드에서 첫 번째 매개 변수를 두 번 제출했습니다.

이제 시도 할 때 406 오류가 반환되지만 적어도 401 문제가 해결되었습니다.

0

나는 매우 비슷한 이슈 (그리고 나는 그것을 보면서 같은 블로그에서 같은 코드를 가져 왔을 것 같다)를 가지고있다.

나는 모든 값을 대문자로 사용하면 스트리밍 API에 대한 track = 매개 변수의 값에 대해 401 오류가 발생하는 것을 막았습니다.

관련 문제