为了让 BitBucket OAuth API 正常工作,我已经把头撞在桌子上大约一整周了,但我什至无法获得一个请求令牌来挽救我自己的生命。我在此处编写的代码适用于 LinkedIn,但 BitBucket 始终返回 HTTP 状态代码 400,并带有非常详细且有用的消息:“无法验证 OAuth 请求”。
这是一个简单的代码(50 行,不能让它更短),之所以这样,是因为我需要手动实现 OAuth,否则我不会在这里询问并使用其他外部库,但由于公司的要求,我不是允许在此项目中使用外部库。不过我看不出有什么问题,1.0a 并不难,获取请求令牌不应该花这么长时间。有什么问题?
我还检查了我的时间戳,这很好,针对 pool.ntp.org 的 w32tm.exe 以 +30 或其他值返回时间。我还尝试在 UtcNow 时间戳中添加和删除 30 分钟,但没有成功,但我的时钟已正确同步(与本地时间和正确的 GMT 值 (GMT -4:30)),因此根本没有任何意义。
可能是因为我位于公司防火墙 (Forefront) 后面吗?但是,为什么 LinkedIn 的 API 调用有效而 BitBucket 无效?我还阅读了很多文档,例如 OAuth 圣经、RFC、官方文档等。当然,在询问之前对 SO 进行了广泛搜索,并在点击之前查看了“类似问题”面板中显示的所有链接“发布您的问题”按钮。
这是简单的代码(C#):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Security.Cryptography;
namespace OAuthTest1
{
class Program
{
static void Main(string[] args)
{
String key = "KEY";
String secret = "SECRET";
String requestUrl = "https://bitbucket.org/api/1.0/oauth/request_token";
string sigBaseStringParams = "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + GetTimeStamp();
sigBaseStringParams += "&" + "oauth_callback=http%3A%2F%2Flocal%3Fdump";
sigBaseStringParams += "&" + "oauth_version=1.0";
string sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(requestUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);
string signature = GetSignature(sigBaseString, secret);
Console.WriteLine(PostData(requestUrl, sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature)));
Console.ReadKey();
}
public static string GetNonce()
{
return new Random().Next(1000000000).ToString();
}
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
public static String PostData(string url, string postData)
{
WebClient w = new WebClient();
return w.UploadString(url, postData);
}
public static string GetSignature(string sigBaseString, string consumerSecretKey, string requestTokenSecretKey = null)
{
HMACSHA1 hmacsha1 = new HMACSHA1();
String signingKey = string.Format("{0}&{1}", consumerSecretKey, !string.IsNullOrEmpty(requestTokenSecretKey) ? requestTokenSecretKey : "");
hmacsha1.Key = Encoding.UTF8.GetBytes(signingKey);
return Convert.ToBase64String(hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(sigBaseString)));
}
}
}
编辑:这是按请求捕获的提琴手和浏览器:
提前致谢!
编辑 2:我使用 n0741337 的建议提供的OAuthBase.cs类进行了新测试,但是,该类不符合要求回调方法的 1.0A 规范(至少 Bitbucket 可以这么说这样的参数是必需的)所以我不得不修改它,以便它在基本签名字符串中包含回调参数(以未编码的原始格式)。虽然结果相同(我认为如果我显示我的密钥并不重要,因为无论如何你都不会看到我的密钥),这是一个捕获:
这是签名基本字符串:
GET&https%3A%2F%2Fbitbucket.org%2Fapi%2F1.0%2Foauth%2Frequest_token&oauth_callback%3Dhttp%3A%2F%2Flocalhost%3A4000%2F%26oauth_consumer_key%3Dkey%26oauth_nonce%3D8231861%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1423671576%26oauth_version%3D1.0
另外,为了让我的代码受到质疑,也可以使用我发现有人已经适应 Twitter 的这个类(尽管 Twitter 在我的工作场所被阻止,所以我无法对其进行测试),但结果是一样的。
这是我制作的使用此类类的新代码:
using System;
using System.Net;
using System.Security.Cryptography;
using OAuth;
namespace OAuthTest1
{
public class oauth2
{
public static void Run()
{
OAuthBase a = new OAuthBase();
String nonce = a.GenerateNonce();
String ts = a.GenerateTimeStamp();
String key = "key";
String secret = "secret";
String requestUrl = "https://bitbucket.org/api/1.0/oauth/request_token";
String normalizedUrl, normalizedArgs;
String sigBase = a.GenerateSignatureBase(
new Uri(requestUrl),
key,
null,
secret,
"http://localhost:4000/",
"GET",
ts,
nonce,
"HMAC-SHA1",
out normalizedUrl,
out normalizedArgs
);
String sig = a.GenerateSignatureUsingHash(sigBase, new HMACSHA1());
String GETArgs = String.Empty;
GETArgs += "oauth_consumer_key=" + key;
GETArgs += "&oauth_nonce=" + nonce;
GETArgs += "&oauth_signature_method=HMAC-SHA1";
GETArgs += "&oauth_timestamp=" + ts;
GETArgs += "&oauth_version=1.0";
GETArgs += "&oauth_callback=" + a.UrlEncode("http://localhost:4000/");
GETArgs += "&oauth_signature=" + sig;
WebClient w = new WebClient();
Console.WriteLine(w.DownloadString(requestUrl + "?" + GETArgs));
Console.ReadKey();
}
}
}
编辑 2 的问题:有谁知道通过这种方法连接到 Bitbucket 服务的应用程序,以便我可以运行 Fiddler 并查看它发送的内容?如果我能看到一些输出,我至少可以复制流程:/ 我已经尝试过 SourceTree,但效果不是很好。
编辑 3:根据 AZ. 的建议,我用这个更改了时间戳生成代码,但它无论如何都不起作用:(。时间戳值看起来不错,但我的时间戳和服务器的时间戳之间只有 5 秒的细微差别:
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
String timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString();
另外,我注意到签名包含一个“+”,它应该被编码为 %20,当我在编辑问题后注意到这一点时我这样做了,它也不起作用,仅供参考。