.NET 7调用Twitter API V2遇Unsupported Authentication问题求助
排查Twitter API V2 OAuth 1.0a认证错误(Unsupported Authentication)
核心问题分析及修复方案
时间戳格式错误:
代码中时间戳使用TotalSeconds直接转字符串,会生成带小数的数值(如1699999999.123),但Twitter要求Unix时间戳必须是整数秒数。这会导致签名验证失败,触发认证错误。
修复:将时间戳转换为长整型:var timeStamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;Authorization头缺失oauth_token参数:
签名生成逻辑中包含了oauth_token参数,但实际发送的Authorization头里没有这个字段,导致签名与请求参数不匹配,Twitter无法识别认证方式。
修复:在Authorization头中添加oauth_token:request.AddHeader("Authorization", $"OAuth oauth_consumer_key=\"{consumerKey}\",oauth_token=\"{accessToken}\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"{timeStamp}\",oauth_nonce=\"{nonce}\",oauth_version=\"1.0\",oauth_signature=\"{signature}\"");不必要的消息编码:
对message做了HttpUtility.UrlEncode,但请求体是JSON格式,JSON会自动处理转义,提前编码会导致推文内容出现%20这类编码字符,影响内容正确性。
修复:移除encodedMessage,直接使用原始message:request.AddJsonBody(new { text = message });
修改后的完整代码示例
public async Task<ActionResult> SendTweet([FromQuery] string accessToken, [FromQuery] string secret, [FromQuery] string message) { string consumerKey = "YOUR_CONSUMER_KEY"; string consumerSecret = "YOUR_CONSUMER_SECRET"; var nonce = Guid.NewGuid().ToString(); // 修正时间戳为整数秒数 var timeStamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; string signature = GetSignatureBaseString("https://api.twitter.com/2/tweets", timeStamp.ToString(), nonce, consumerKey, accessToken, consumerSecret, secret); signature = HttpUtility.UrlEncode(signature); var client = new RestClient("https://api.twitter.com/2/tweets"); var request = new RestRequest(Method.Post); request.AddHeader("content-type", "application/json"); // 修复Authorization头,添加oauth_token参数 request.AddHeader("Authorization", $"OAuth oauth_consumer_key=\"{consumerKey}\",oauth_token=\"{accessToken}\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"{timeStamp}\",oauth_nonce=\"{nonce}\",oauth_version=\"1.0\",oauth_signature=\"{signature}\""); // 移除不必要的UrlEncode,直接使用原始message request.AddJsonBody(new { text = message }); RestResponse response = await client.ExecuteAsync(request); Console.WriteLine(response.Content); return Ok(response.Content); } private static string GetSignatureBaseString(string url, string timeStamp, string nonce, string consumerKey, string accessToken, string consumerSecret, string secret) { string signatureBaseString = "POST&"; signatureBaseString += Uri.EscapeDataString(url); signatureBaseString += "&"; var parameters = new SortedDictionary<string, string> { {"oauth_consumer_key", consumerKey}, {"oauth_token", accessToken}, {"oauth_signature_method", "HMAC-SHA1"}, {"oauth_timestamp", timeStamp}, {"oauth_nonce", nonce}, {"oauth_version", "1.0"} }; bool first = true; StringBuilder keyValuePairs = new StringBuilder(); foreach (var kvp in parameters) { if (!first) keyValuePairs.Append("&"); keyValuePairs.Append($"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}"); first = false; } signatureBaseString += Uri.EscapeDataString(keyValuePairs.ToString()); string signatureKey = $"{consumerSecret}&{secret}"; using var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(signatureKey)); byte[] signatureBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(signatureBaseString)); return Convert.ToBase64String(signatureBytes); }
额外验证点
- 确认
accessToken和secret参数从QueryString正确传递,没有被篡改或编码错误。 - 检查消费者密钥和令牌是否与Postman中使用的完全一致,无空格或拼写错误。
内容的提问来源于stack exchange,提问作者Álex
相关产品推荐
相关产品推荐

