.NET 中的 OAuth 和验证

2023-11-27

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

这是我第一次使用 OAuth,上手时遇到很多困难。我找到了几个 .NET OAuth 库或帮助程序,但它们似乎比我想象的更复杂。我想要做的就是能够向 Mendeley API 发出 REST 请求并获得响应!

到目前为止,我已经尝试过:

  • DotNetOpenAuth
  • http://github.com/bittercoder/DevDefined.OAuth
  • http://oauth.googlecode.com/svn/code/csharp/

第一个(DotNetOpenAuth)似乎可以做我需要的事情,如果我花了几个小时试图弄清楚如何做。据我所知,第二个和第三个不支持 Mendeley 发回的验证码——尽管我对此可能是错的:)

我从 Mendeley 获得了消费者密钥和秘密,并且通过 DotNetOpenAuth,我成功启动了一个浏览器,其中包含 Mendeley 页面,为用户提供验证码以进入应用程序。然而,此时我迷失了方向,无法弄清楚如何明智地将其返回给应用程序。

我非常愿意承认我不知道从哪里开始(尽管似乎有一个相当陡峭的学习曲线) - 如果有人能指出我正确的方向,我将不胜感激!


我同意你的看法。可用于 .NET 应用程序的开源 OAuth 支持类很难理解、过于复杂(DotNetOpenAuth 公开了多少方法?)、设计不佳(查看来自 google 的 OAuthBase.cs 模块中具有 10 个字符串参数的方法)您提供的链接 - 根本没有状态管理),或者其他方面不满意。

不需要这么复杂。

我不是 OAuth 方面的专家,但我已经制作了一个 OAuth 客户端管理器类,我成功地将其与 Twitter 和 TwitPic 一起使用。使用起来相对简单。它是开源的,可在此处获取:Oauth.cs

回顾一下,在 OAuth 1.0a 中……有点有趣,有一个特殊的名称,它看起来像一个“标准”,但据我所知,实现“OAuth 1.0a”的唯一服务是 Twitter。我想这是标准的enough。好的,无论如何,在 OAuth 1.0a 中,它的工作方式对于桌面应用程序这是:

  1. 您,应用程序的开发者,注册应用程序并获得“消费者密钥”和“消费者秘密”。在 Arstechnica 上,有详细分析了为什么这个模型不是最好的,但正如他们所说,就是这样.

  2. 您的应用程序运行。第一次运行时,它需要让用户明确批准应用程序向 Twitter 及其姐妹服务(如 TwitPic)发出经过 oauth 身份验证的 REST 请求。为此,您必须经过批准流程,包括用户的明确批准。这种情况仅在应用程序第一次运行时发生。像这样:

    • 请求“请求令牌”。又名临时令牌。
    • 弹出一个网页,将该请求令牌作为查询参数传递。此网页向用户呈现 UI,询问“您想授予对此应用程序的访问权限吗?”
    • 用户登录 Twitter 网页,并授予或拒绝访问权限。
    • 出现响应 html 页面。如果用户已授予访问权限,则会以 48 磅字体显示 PIN
    • 用户现在需要将该引脚剪切/粘贴到 Windows 表单框中,然后单击“下一步”或类似的操作。
    • 然后,桌面应用程序会对“访问令牌”发出经过 oauth 身份验证的请求。另一个 REST 请求。
    • 桌面应用程序接收“访问令牌”和“访问密钥”。

在批准舞蹈之后,桌面应用程序可以仅使用用户特定的“访问令牌”和“访问秘密”(以及应用程序特定的“消费者密钥”和“消费者秘密”)代表用户执行经过身份验证的请求到推特。这些不会过期,但如果用户取消对应用程序的授权,或者 Twitter 出于某种原因取消对您的应用程序的授权,或者如果您丢失了访问令牌和/或秘密,则您需要再次进行批准舞蹈。


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

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

Like this:
alt text


如果您已经对 UI 进行了排序,那么剩下的唯一挑战就是生成 oauth 签名的请求。这让很多人感到困惑,因为认证签名要求有些特殊。这就是简化的 OAuth Manager 类的作用。

请求令牌的示例代码:

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");

就是这样。简单的。从代码中可以看出,获取 oauth 参数的方法是通过基于字符串的索引器,类似于字典。 AcquireRequestToken 方法将 oauth 签名的请求发送到授予请求令牌(也称为临时令牌)的服务的 URL。对于 Twitter,此 URL 是“https://api.twitter.com/oauth/request_token“。oauth 规范表示,您需要以某种方式(url 编码并由&符号连接)打包 oauth 参数集(token、token_secret、nonce、timestamp、consumer_key、version 和callback),并以按字典顺序排序,在该结果上生成签名,然后将这些相同的参数与签名一起打包,以不同的方式存储在新的 oauth_signature 参数中(用逗号连接)。OAuth 管理器类会自动为您完成此操作。它生成随机数、时间戳和版本和签名自动 - 您的应用程序不需要关心或了解这些东西。只需设置 oauth 参数值并进行简单的方法调用即可。管理器类发出请求并为您解析响应。

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

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

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

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

当用户单击“允许”按钮时,将加载新页面。它是一个 HTML 表单,其工作方式与在完整浏览器中相同。在代码中,为 WebBrowser 控件的 DocumentedCompleted 事件注册一个处理程序,然后在该处理程序中获取引脚:

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 屏幕抓取。

获取 PIN 码后,您不再需要网络浏览器,因此:

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

...并且您可能还想对其调用 Dispose() 。

下一步是通过与该 pin 一起发送另一条 HTTP 消息来获取访问令牌。这是另一个签名的 oauth 调用,使用我上面描述的 oauth 排序和格式构建。但使用 OAuth.Manager 类这又非常简单:

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".

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

要与第三方服务(例如 TwitPic)交互,您需要构建一个稍微不一样OAuth 标头,如下所示:

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

对于 Twitter,验证信用 url 和领域的值为“https://api.twitter.com/1/account/verify_credentials.json", and "http://api.twitter.com/“ 分别。

...然后把thatHTTP 标头中的授权字符串称为X-验证-凭据-授权。然后将其与您发送的任何请求一起发送到您的服务,例如 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.Manager 处理传出 oauth 请求的生成以及响应中 oauth 内容的接收和处理。当 Request_token 请求为您提供 oauth_token 时,您的应用程序不需要存储它。 Oauth.Manager 足够智能,可以自动执行此操作。同样,当 access_token 请求返回访问令牌和机密时,您不需要显式存储它们。 OAuth.Manager 为您处理该状态。

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

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);
}

您可以下载此处包含 OAuth.Manager 类的 DLL。该下载中还有一个帮助文件。或者你可以在线查看帮助文件.

查看使用此管理器的 Windows 窗体示例here.


工作示例

下载工作示例使用此处描述的类和技术的命令行工具:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET 中的 OAuth 和验证 的相关文章

  • 如何在 REST WCF 服务中接受任意 JSON 对象?

    我想实现这样的服务方法 OperationContract WebInvoke RequestFormat WebMessageFormat Json ResponseFormat WebMessageFormat Json public
  • Google Fit O 身份验证失败并显示 RESULT_CANCELED,且不显示权限页面

    您好 我有一个 android 项目 它从应用程序用户的 Google Fit 个人资料中读取某些数据 我遇到了如下奇怪的情况 以下所有内容都处于调试模式 而不是发布模式 1 当从我自己的笔记本电脑编译时 oAuth 在我的手机上成功运行
  • 使用反应式扩展按组缓冲,嵌套订阅

    我有一个事件源 它生成属于某些组的事件 我想缓冲这些组并将这些组 批量 发送到存储 到目前为止我有这个 eventSource GroupBy event gt event GroupingKey Select group gt new g
  • 禁用数据表更改跟踪

    有没有办法让数据表自动接受更改或在修改行时禁用更改跟踪 当更改数据表时 忘记调用DataTable AcceptChanges 可能会导致真正的问题 我认为整个要点DataTable是具有行版本控制和更改跟踪的额外好处 我认为您实际上无法禁
  • 异步protobuf序列化

    protobuf net 实现的一个限制是它同步调用底层流 通过不提供异步 API 例如BeginSerialize EndSerialize 或 TPL 等效项 我们被迫占用一个等待同步流 I O 的线程 是否有计划在 protobuf
  • 进程间并发文件写入

    我需要将不同进程的日志数据写入单个文件 我正在使用 Windows Mutex 它需要公共语言运行时支持 Mutex m gcnew Mutex false MyMutex m gt WaitOne File Open and Write
  • 为什么我的 WPF XAML Grid TranslateTransform.X 不会?

    我可以使用它来更改网格的宽度 高度 那么为什么当我这样使用 Grid RenderTransform TranslateTransform X 时它不起作用
  • 找不到文件异常..但它就在那里

    嘿 这将是那些愚蠢的问题之一 我试图在本地系统上获取一个文件 但我不断收到FileNotFoundException thrown 请有人纠正我 if File Exists C logs hw healthways prod 2009 0
  • 获取字母数字值的 Max()

    我有一个包含字母数字 ID 的字典 例如 a10a10 和 d10a9 我想要其中最大的 ID 意思是 9 当我使用以下代码时 d10a9 是 MAX 因为 9 排在 10 之前 var lsd new Dictionary
  • 使用 OAuth 的 Vkontakte API 不适用于验证码

    Vkontakte API 拒绝使用验证码的请求 这就是我所做的例子 在第三个请求之后我得到 验证码错误 14 需要验证码 根据添加就足够了captcha sid and captcha key参数到原始请求并重复请求 但回应依然是Capt
  • 如何在构建后事件中获取版本号

    我想使用构建后事件自动创建 nuget 包 然后将其复制到我们网络上的共享文件夹 如下所示 版本号 1 0 0 0 在 MyLib nuspec 文件中指定 nuget exe pack SolutionDir MyLib nuspec x
  • 简单的文档管理系统和API [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • .Net 中是否有一个框架属性可以隐藏成员以使其免受反射?

    是否有一个属性可以隐藏成员 特别是属性 typeof MyType GetProperties 在 net中 我正在寻找快速修复方法 即不创建自定义属性等 thanks No 反射允许人们看到一切 包括标记为私有的成员 最终反射使用与 CL
  • 哪些 .NET 编程语言有 CodeDom 提供程序?

    除了 C VB NET C 托管和 C CLI 和 F 之外 哪些 NET 编程语言有自己的 CodeDom 提供程序 我考虑过为 UnrealScript 制作一个 但我遇到了一些奇怪的问题 比如代码二进制运算符类型 http msdn
  • Linq 在 .Substring() 上抛出异常

    我遇到了一种情况 我需要让 LINQ to Entities 查询根据字符串的长度返回一个子字符串 这是查询 var query from f in Context Files orderby f DateAdded descending
  • 如何在 C# windows 窗体应用程序中将焦点发送到 tabindex 低于当前控件的控件?

    在我的 C Windows 窗体应用程序中 我的窗体中有许多文本框控件 用户在其中输入要保存在数据库中的数据 我希望用户能够通过向上和向下箭头将焦点更改到每个文本框 我可以这样做对于向下箭头 因此当用户按下向下箭头时 焦点将通过 SendK
  • .NET 图形重影

    我正在为我们正在开发的新应用程序制作一个示例 GUI 我已经决定了语言 但我可以使用任何第 3 方 DLL 或插件或任何我需要的东西 以使 GUI 尽可能无缝地工作 他们希望它非常像 mac ubuntu vista Windows 7 所
  • Parallel.For 和 Break() 误解?

    我正在研究 For 循环中的并行性中断 看完之后this http tipsandtricks runicsoft com CSharp ParallelClass html and this http reedcopsey com 201
  • 将参数从 Web 表单传递到 Crystal 报表

    我有一份报告 我想将其显示在网络表单上 没有参数的报告运行良好 带参数的报告让我很头疼 这是我在 BindReport 方法中编写的代码 该代码在表单的页面加载事件上调用 ReportDocument rpt new ReportDocum
  • 使用 LINQ to SQL 的 .NET 架构的最佳设计实践(DAL 必要吗?我们真的可以使用 POCO吗?要采用的设计模式吗?)

    我避免在 net arch n 层架构上编写看起来像是另一个线程的内容 但请耐心等待 希望我和其他人一样 在选择用于企业应用程序的架构时 考虑到当今的趋势和新兴技术 仍然没有 100 满意或不清楚应采取的最佳方法 我想我正在寻求大众社区对方

随机推荐