Twitter的OAuth的授权问题与.NET(C#)(Twitter OAuth Authoriz

2019-09-20 10:45发布

我一直在拉我的头发推测我们有什么错在这里不用,显然我一直没能到。 我尝试创建一个基本的结构,在.NET(C#)Twitter的API在OAuth的工作,但什么是错的,我看不出那是什么。

例如,当我发送,以便获得一个请求令牌请求 ,我回来401未授权响应返回与消息如下:

无法验证的OAuth签名和令牌

我用它来创建签名如下签名基地(我代替我的实际消费主要与虚假值):

POST&HTTP%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dhttp%3A%2F%2Flocalhost%3A44444%2Faccount%2Fauth%26oauth_consumer_key%3XXXXXXXXXXXXXXXXXXXXXXXXXXX%26oauth_nonce%3DNjM0NzkyMzk0OTk2ODEyNTAz%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1343631900%26oauth_version% 3D1.0

签名密钥只能由我的用户端密钥,并鉴于一个符号的,我没有一个令牌密钥产品尚未推出(再次,我代替我的实际消费主要与虚假值):

signingKey:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&

最后,我结束了以下授权标题(再次,虚拟消费密钥):

的OAuth oauth_callback = “HTTP%3A%2F%2Flocalhost%3A44444%2Faccount%2Fauth”,oauth_consumer_key = “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,oauth_nonce = “NjM0NzkyMzk0OTk2ODEyNTAz”,oauth_signature_method = “HMAC-SHA1”,oauth_timestamp = “1343631900”,oauth_version = “1.0” ,oauth_signature = “ttLvZ2Xzq4CHt%2BNM4pW7X4h1wRA%3D”

我用这个代码如下(这是一个有点长,但我会在这里,而粘贴给予要点URL或东西代替):

public class OAuthMessageHandler : DelegatingHandler {

    private const string OAuthConsumerKey = "oauth_consumer_key";
    private const string OAuthNonce = "oauth_nonce";
    private const string OAuthSignature = "oauth_signature";
    private const string OAuthSignatureMethod = "oauth_signature_method";
    private const string OAuthTimestamp = "oauth_timestamp";
    private const string OAuthToken = "oauth_token";
    private const string OAuthVersion = "oauth_version";
    private const string OAuthCallback = "oauth_callback";

    private const string HMACSHA1SignatureType = "HMAC-SHA1";

    private readonly OAuthState _oAuthState;

    public OAuthMessageHandler(OAuthCredential oAuthCredential, OAuthSignatureEntity signatureEntity,
        IEnumerable<KeyValuePair<string, string>> parameters, HttpMessageHandler innerHandler) : base(innerHandler) {

        _oAuthState = new OAuthState() {
            Credential = oAuthCredential,
            SignatureEntity = signatureEntity,
            Parameters = parameters,
            Nonce = GenerateNonce(),
            SignatureMethod = GetOAuthSignatureMethod(),
            Timestamp = GetTimestamp(),
            Version = GetVersion()
        };
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {

        //add the auth header
        request.Headers.Authorization = new AuthenticationHeaderValue(
            "OAuth", GenerateAuthHeader(_oAuthState, request)
        );

        return base.SendAsync(request, cancellationToken);
    }

    private string GetTimestamp() {

        TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        return Convert.ToInt64(timeSpan.TotalSeconds).ToString();
    }

    private string GetVersion() {

        return "1.0";
    }

    private string GetOAuthSignatureMethod() {

        return HMACSHA1SignatureType;
    }

    private string GenerateNonce() {

        return Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
    }

    private string GenerateSignature(OAuthState oAuthState, HttpRequestMessage request) {

        //https://dev.twitter.com/docs/auth/creating-signature
        //http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/

        SortedDictionary<string, string> signatureCollection = new SortedDictionary<string, string>();

        //Required for all requests
        signatureCollection.Add(OAuthConsumerKey, oAuthState.Credential.ConsumerKey);
        signatureCollection.Add(OAuthNonce, oAuthState.Nonce);
        signatureCollection.Add(OAuthVersion, oAuthState.Version);
        signatureCollection.Add(OAuthTimestamp, oAuthState.Timestamp);
        signatureCollection.Add(OAuthSignatureMethod, oAuthState.SignatureMethod);

        //Parameters
        if (oAuthState.Parameters != null) {
            oAuthState.Parameters.ForEach(x => signatureCollection.Add(x.Key, x.Value));
        }

        //Optionals
        if (!string.IsNullOrEmpty(oAuthState.Credential.Token))
            signatureCollection.Add(OAuthToken, oAuthState.Credential.Token);

        if (!string.IsNullOrEmpty(oAuthState.Credential.CallbackUrl))
            signatureCollection.Add(OAuthCallback, oAuthState.Credential.CallbackUrl);

        //Build the signature
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.AppendFormat("{0}&", request.Method.Method.ToUpper());
        strBuilder.AppendFormat("{0}&", Uri.EscapeDataString(request.RequestUri.ToString()));
        signatureCollection.ForEach(x =>
            strBuilder.Append(
                Uri.EscapeDataString(string.Format("{0}={1}&", x.Key, x.Value))
            )
        );

        //Remove the trailing ambersand char from the signatureBase.
        //Remember, it's been urlEncoded so you have to remove the
        //last 3 chars - %26
        string baseSignatureString = strBuilder.ToString();
        baseSignatureString = baseSignatureString.Substring(0, baseSignatureString.Length - 3);

        //Build the signing key
        string signingKey = string.Format(
            "{0}&{1}", Uri.EscapeDataString(oAuthState.SignatureEntity.ConsumerSecret),
            string.IsNullOrEmpty(oAuthState.SignatureEntity.OAuthTokenSecret) ? "" : Uri.EscapeDataString(oAuthState.SignatureEntity.OAuthTokenSecret)
        );

        //Sign the request
        using (HMACSHA1 hashAlgorithm = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey))) {

            return Convert.ToBase64String(
                hashAlgorithm.ComputeHash(
                    new ASCIIEncoding().GetBytes(baseSignatureString)
                )
            );
        }
    }

    private string GenerateAuthHeader(OAuthState oAuthState, HttpRequestMessage request) {

        SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>();
        sortedDictionary.Add(OAuthNonce, Uri.EscapeDataString(oAuthState.Nonce));
        sortedDictionary.Add(OAuthSignatureMethod, Uri.EscapeDataString(oAuthState.SignatureMethod));
        sortedDictionary.Add(OAuthTimestamp, Uri.EscapeDataString(oAuthState.Timestamp));
        sortedDictionary.Add(OAuthConsumerKey, Uri.EscapeDataString(oAuthState.Credential.ConsumerKey));
        sortedDictionary.Add(OAuthVersion, Uri.EscapeDataString(oAuthState.Version));

        if (!string.IsNullOrEmpty(_oAuthState.Credential.Token))
            sortedDictionary.Add(OAuthToken, Uri.EscapeDataString(oAuthState.Credential.Token));

        if (!string.IsNullOrEmpty(_oAuthState.Credential.CallbackUrl))
            sortedDictionary.Add(OAuthCallback, Uri.EscapeDataString(oAuthState.Credential.CallbackUrl));

        StringBuilder strBuilder = new StringBuilder();
        var valueFormat = "{0}=\"{1}\",";

        sortedDictionary.ForEach(x => { 
            strBuilder.AppendFormat(valueFormat, x.Key, x.Value); 
        });

        //oAuth parameters has to be sorted before sending, but signature has to be at the end of the authorization request
        //http://stackoverflow.com/questions/5591240/acquire-twitter-request-token-failed
        strBuilder.AppendFormat(valueFormat, OAuthSignature, Uri.EscapeDataString(GenerateSignature(oAuthState, request)));

        return strBuilder.ToString().TrimEnd(',');
    }

    private class OAuthState {

        public OAuthCredential Credential { get; set; }
        public OAuthSignatureEntity SignatureEntity { get; set; }
        public IEnumerable<KeyValuePair<string, string>> Parameters { get; set; }
        public string Nonce { get; set; }
        public string Timestamp { get; set; }
        public string Version { get; set; }
        public string SignatureMethod { get; set; }
    }
}

还有的混合新的.NET HttpClient在这里,但授权头生成的代码是明确的理解。

所以,你会在这里是我的问题,我缺少什么?

编辑:

我给它不同的端点(如/1/account/update_profile.json)一试,和它的作品的时候我送他的尸体不需要编码的请求。 如: location=Marmaris的作品,但location=Marmaris, Turkey不能使用,即使我对其进行编码,使用工作Uri.EscapeDataString

编辑:

我给它通过Twitter OAuth工具一试,看看是否有我的签名基地和Twitter的之间的任何特别的差异,我可以看到Twitter的编码比我的不同。 例如,微博产生location%3DMarmaris%252C%2520Turkeylocation=Marmaris, Turkey但我产生是location%3DMarmaris%2C%20Turkey

Answer 1:

似乎所有的问题涉及到编码的问题,现在,我似乎解决了这个问题了。 下面是关于这一问题的一些信息:

当我们正在创建的签名基地,参数值需要编码的两倍。 例如,我有如下的集合:

var collection = new List<KeyValuePair<string, string>>();
collection.Add(new KeyValuePair<string, string>("location", Uri.EscapeDataString(locationVal)));
collection.Add(new KeyValuePair<string, string>("url", Uri.EscapeDataString(profileUrl)));

当我通过这GenerateSignature方法,该方法编码这些更多的时间,这导致百分号编码为%25 ,这使得它的工作。 我的一个问题是解决了,但我还是没能在那个时候成功地发出请求令牌请求。

然后,我看着“request_token”的要求,看到下面的一行代码:

OAuthCredential creds = new OAuthCredential(_consumerKey) {
    CallbackUrl = "http://localhost:44444/account/auth"
};

我送CallbackUrl ,因为它是为了它进行编码,但如Twitter需要编码的东西两次签名的基础上,我认为这可能是问题。 然后,我换成这个代码与以下之一:

OAuthCredential creds = new OAuthCredential(_consumerKey) { 
    CallbackUrl = Uri.EscapeDataString("http://localhost:44444/account/auth")
};

我做了一个详细的变化是内部GenerateAuthHeader方法,因为我并不需要进行编码CallbackUrl两次。

//don't encode it here again.
//we already did that and auth header doesn't require it to be encoded twice
if (!string.IsNullOrEmpty(_oAuthState.Credential.CallbackUrl))
    sortedDictionary.Add(OAuthCallback, oAuthState.Credential.CallbackUrl);

而我做到了运行W / O型的任何问题。



Answer 2:

这里Tugberk是另一个OAuth的LIB,这里没有魔法。

http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs



文章来源: Twitter OAuth Authorization Issue with .NET (C#)