我正在try 创建一个基于.NET的客户端应用程序(在WPF中-尽管目前我只是将其作为控制台应用程序)来与支持OAuth的应用程序集成,特别是Mendeley(http://dev.mendeley.com),它显然使用3条腿的OAuth.

这是我第一次使用OAuth,我在开始使用它时遇到了很多困难.我找到了几个.NET OAuth库或助手,但它们似乎比我认为需要的更复杂.我想做的就是能够向Mendeley API发出REST请求并获得回复!

到目前为止,我已经try 过:

第一个(DotNetOpenAuth)似乎可以做我需要的事情,如果我花了好几个小时试图解决这个问题.第二个和第三个,正如我所能说的,不支持门捷利发回的验证码——尽管我可能错了:)

我从Mendeley那里得到了一个消费者密钥和秘密,通过DotNetOpenAuth,我成功地启动了一个浏览器,其中包含Mendeley页面,为用户输入应用程序提供了验证码.然而,在这一点上,我迷路了,不知道如何明智地将其提供给应用程序.

我很愿意承认,我不知道从哪里开始(虽然这似乎是一个相当陡峭的学习曲线)——如果有人能给我指出正确的方向,我将不胜感激!

推荐答案

我同意你的看法.开放源码的OAuth支持类可用于.NET应用程序很难理解,过于复杂(DotNetOpenAuth公开了多少方法?),设计拙劣(看看你提供的谷歌链接中OAuthBase.cs模块中有10个字符串参数的方法——根本没有状态管理),或者其他不令人满意的方法.

没必要这么复杂.

我不是OAuth方面的专家,但我已经开发了一个OAuth客户端管理器类,我成功地将其用于Twitter和TwitPic.它的使用相对简单.它是开源的,可在这里获得:Oauth.cs

回顾一下,在OAuth1.0a中……有点好笑,它有一个特殊的名字,看起来像一个"标准",但据我所知,唯一实现"OAuth1.0a"的服务是Twitter.我想那是标准的enough.好的,不管怎样,在OAuth 1.0a中,它for desktop apps的工作方式是这样的:

  1. 你,应用程序的开发者,注册应用程序,并获得一个"消费者密钥"和"消费者秘密".在Arstechnica,有a well written analysis of why this model isn't the best个,但就像他们说的,it is what it is个.

  2. 您的应用程序正在运行.在第一次运行时,它需要用户明确批准该应用程序向twitter及其姊妹服务(如twitPic)发出oauth认证的睡觉请求.要做到这一点,您必须通过一个审批流程,该流程涉及用户的显式审批.这只会在应用程序第一次运行时发生.如下所示:

    • 请求"请求令牌".又名临时 token .
    • 弹出一个网页,将该请求令牌作为查询参数传递.此网页向用户显示UI,询问"是否要授予此应用的访问权限?"
    • 用户登录twitter网页,并授予或拒绝访问权限.
    • 此时会出现响应html页面.如果用户已授予访问权限,则会以48磅字体显示PIN
    • 用户现在需要将该pin剪切/粘贴到windows窗体框中,然后单击"下一步"或类似操作.
    • 然后,桌面应用程序对"访问令牌"执行oauth身份验证请求.另一个休息请求.
    • 桌面应用程序接收"访问令牌"和"访问密钥".

在批准舞蹈之后,桌面应用程序只需使用特定于用户的"访问令牌"和"访问密钥"(以及特定于应用程序的"消费者密钥"和"消费者密钥")就可以代表用户向Twitter发出经过身份验证的请求.这些不会过期,尽管如果用户取消了应用程序的授权,或者如果Twitter出于某种原因取消了你的应用程序的授权,或者如果你丢失了访问令牌和/或密码,你需要再次进行审批.


如果您不聪明,UI流可以在某种程度上反映多步骤OAuth消息流.还有更好的办法.

使用WebBrowser控件,并在桌面应用程序中打开授权网页.当用户单击"允许"时,从WebBrowser控件获取响应文本,自动提取PIN,然后获取访问令牌.您发送5或6个HTTP请求,但用户只需要看到一个允许/拒绝对话框.易于理解的

Like this:
alt text


如果已经对UI进行了排序,那么剩下的唯一挑战就是生成oauth签名的请求.这让很多人感到困惑,因为oauth的签名要求有点特殊.这就是简化的OAuth管理器类所做的.

请求令牌的示例代码:

var oauth = new OAuth.Manager();
// the URL to obtain a temporary "request token"
var rtUrl = "https://api.twitter.com/oauth/request_token";
oauth["consumer_key"] = MY_APP_SPECIFIC_KEY;
oauth["consumer_secret"] = MY_APP_SPECIFIC_SECRET;    
oauth.AcquireRequestToken(rtUrl, "POST");

THAT'S IT.简单.从代码中可以看到,获取oauth参数的方法是通过基于字符串的索引器,类似于字典.AcquisiteRequestToken方法将oauth签名的请求发送到授予请求令牌(也称为临时令牌)的服务的URL.对于Twitter,这个URL是"https://api.twitter.com/oauth/request_token".oauth规范说,您需要以某种方式(url编码并由符号连接)打包oauth参数集(令牌、令牌\u secret、nonce、时间戳、使用者\u密钥、版本和回调),并按照字典排序的顺序,对该结果生成签名,然后将这些相同的参数与签名一起打包,以不同的方式存储在新的oauth_签名参数中(用逗号连接).The OAuth manager class does this for you automatically.它会自动生成nonce、时间戳和版本and signatures——你的应用不需要关心或意识到这些东西.只需设置oauth参数值并进行简单的方法调用.manager类发出请求并为您解析响应.

好吧,然后呢?获得请求令牌后,您将弹出web浏览器UI,用户将在其中明确授予批准.如果你做得对,你会在嵌入式浏览器中弹出它.对于Twitter,其URL为"https://api.twitter.com/oauth/authorize?oauth_token=",并附加了oauth_标记.在这样的代码中执行此操作:

var url = SERVICE_SPECIFIC_AUTHORIZE_URL_STUB + oauth["token"];
webBrowser1.Url = new Uri(url);

(如果在外部浏览器中执行此操作,则会使用System.Diagnostics.Process.Start(url).)

设置Url属性会使WebBrowser控件自动导航到该页面.

当用户单击"允许"按钮时,将加载一个新页面.这是一个HTML表单,其工作原理与在完整浏览器中相同.在代码中,为WebBrowser控件的DocumentedCompleted事件注册一个处理程序,并在该处理程序中获取pin:

var divMarker = "<div id=\"oauth_pin\">"; // the div for twitter's oauth pin
var index = webBrowser1.DocumentText.LastIndexOf(divMarker) + divMarker.Length;
var snip = web1.DocumentText.Substring(index);
var pin = RE.Regex.Replace(snip,"(?s)[^0-9]*([0-9]+).*", "$1").Trim();

这是一些HTML屏幕抓取.

拿到插针后,不再需要网页浏览器,所以:

webBrowser1.Visible = false; // all done with the web UI

...您可能还需要对其调用Dispose().

下一步是获取访问令牌,方法是随pin一起发送另一条HTTP消息.这是另一个有符号的oauth调用,使用我上面描述的oauth排序和格式构造.但再一次,这对于OAuth来说非常简单.经理级:

oauth.AcquireAccessToken(URL_ACCESS_TOKEN,
                         "POST",
                         pin);

对于Twitter来说,这个URL是"https://api.twitter.com/oauth/access_token".

现在您有了访问令牌,可以在签名的HTTP请求中使用它们.这样地:

var authzHeader = oauth.GenerateAuthzHeader(url, "POST");

...其中url是资源端点.要更新用户的状态,它将是"http://api.twitter.com/1/statuses/update.xml?status=Hello".

然后将该字符串设置到名为Authorization的HTTP头中.

要与第三方服务(如TwitPic)交互,需要构造一个slightly different OAuth头,如下所示:

var authzHeader = oauth.GenerateCredsHeader(URL_VERIFY_CREDS,
                                            "GET",
                                            AUTHENTICATION_REALM);

对于Twitter,verify creds url和realm的值分别为"https://api.twitter.com/1/account/verify_credentials.json"和"http://api.twitter.com/".

.并将that个授权字符串放入名为X-Verify-Credentials-Authorization的HTTP头中.然后将其与您发送的任何请求一起发送到您的服务,如TwitPic.

就这样.

总之,更新twitter状态的代码可能是这样的:

// the URL to obtain a temporary "request token"
var rtUrl = "https://api.twitter.com/oauth/request_token";
var oauth = new OAuth.Manager();
// The consumer_{key,secret} are obtained via registration
oauth["consumer_key"] = "~~~CONSUMER_KEY~~~~";
oauth["consumer_secret"] = "~~~CONSUMER_SECRET~~~";
oauth.AcquireRequestToken(rtUrl, "POST");
var authzUrl = "https://api.twitter.com/oauth/authorize?oauth_token=" + oauth["token"];
// here, should use a WebBrowser control. 
System.Diagnostics.Process.Start(authzUrl);  // example only!
// instruct the user to type in the PIN from that browser window
var pin = "...";
var atUrl = "https://api.twitter.com/oauth/access_token";
oauth.AcquireAccessToken(atUrl, "POST", pin);

// now, update twitter status using that access token
var appUrl = "http://api.twitter.com/1/statuses/update.xml?status=Hello";
var authzHeader = oauth.GenerateAuthzHeader(appUrl, "POST");
var request = (HttpWebRequest)WebRequest.Create(appUrl);
request.Method = "POST";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authzHeader);

using (var response = (HttpWebResponse)request.GetResponse())
{
    if (response.StatusCode != HttpStatusCode.OK)
        MessageBox.Show("There's been a problem trying to tweet:" +
                        Environment.NewLine +
                        response.StatusDescription);
}

OAuth 1.0a有点复杂,但使用它并不需要复杂.

在后续运行中,当您已经拥有访问令牌和密码时,可以实例化OAuth.像这样的经理:

var oauth = new OAuth.Manager();
oauth["consumer_key"] = CONSUMER_KEY;
oauth["consumer_secret"] = CONSUMER_SECRET;
oauth["token"] = your_stored_access_token;
oauth["token_secret"] = your_stored_access_secret;

...然后生成如上所述的授权头.

// now, update twitter status using that access token
var appUrl = "http://api.twitter.com/1/statuses/update.xml?status=Hello";
var authzHeader = oauth.GenerateAuthzHeader(appUrl, "POST");
var request = (HttpWebRequest)WebRequest.Create(appUrl);
request.Method = "POST";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authzHeader);

using (var response = (HttpWebResponse)request.GetResponse())
{
    if (response.StatusCode != HttpStatusCode.OK)
        MessageBox.Show("There's been a problem trying to tweet:" +
                        Environment.NewLine +
                        response.StatusDescription);
}

你可以下载a DLL containing the OAuth.Manager class here.下载文件中还有一个帮助文件.或者你可以view the helpfile online.

请参阅使用此管理器here的Windows窗体示例.


工作示例

使用下面描述的类和技术的命令行工具的Download a working example:

.net相关问答推荐

从Couchbase删除_txn文档的推荐方法?""

[x.x.x,)在Packages.lock.json依赖项中是什么意思?

使用React路由加载器获取数据不能正常工作

如何手动注入依赖注入

仅使用 .NET GetBytes 方法转换有效字节而不创建问号

如何在 FtpWebRequest 之前判断 FTP 上是否存在文件

.NET 的 `Array.Sort()` 方法使用的排序算法是稳定的算法吗?

使用 IIS Express 托管网站(临时)

HashSet 是否保留插入顺序?

为什么 .NET 中不需要 Maven?

List 和 IEnumerable 的实际区别

Linq to SQL - 返回前 n 行

自创建数据库以来,支持ApplicationDbContext上下文的模型已更改

如何访问 Session 变量并在 javascript 中设置它们?

如何在 Dapper.Net 中编写一对多查询?

为什么要使用 C# 类 System.Random 而不是 System.Security.Cryptography.RandomNumberGenerator?

在 IIS 中访问 .svc 文件时出现 HTTP 404

可以从 C# 调用 C++ 代码吗?

Roslyn 编译代码失败

枚举和匹配属性的 C# 命名约定