ChatGPT解决这个技术问题 Extra ChatGPT

OAuth 与 .NET 中的验证

我正在尝试创建一个基于 .NET 的客户端应用程序(在 WPF 中 - 尽管目前我只是将其作为控制台应用程序)以与启用 OAuth 的应用程序集成,特别是 Mendeley (http://dev.mendeley.com),这显然使用了 3-legged 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 页面的浏览器,该页面为用户提供了一个验证码以供用户进入应用程序。但是,此时我迷路了,无法弄清楚如何明智地将其提供给应用程序。

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


1
12 revs

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

没必要这么复杂。

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

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

您,应用程序的开发者,注册应用程序并获得“消费者密钥”和“消费者秘密”。在 Arstechnica 上,有一篇写得很好的分析为什么这个模型不是最好的,但正如他们所说,它就是这样。您的应用程序运行。第一次运行时,它需要让用户明确批准应用程序向 Twitter 及其姊妹服务(如 TwitPic)发出经过 oauth 身份验证的 REST 请求。为此,您必须经过一个批准过程,包括用户的明确批准。这仅在应用程序第一次运行时发生。像这样:请求一个“请求令牌”。又名临时令牌。弹出一个网页,将该请求令牌作为查询参数传递。此网页向用户显示 UI,询问“您是否要授予对该应用程序的访问权限?”用户登录到 twitter 网页,并授予或拒绝访问。出现响应 html 页面。如果用户已授予访问权限,则会以 48 磅字体显示 PIN,用户现在需要将该 PIN 剪切/粘贴到 Windows 表单框中,然后单击“下一步”或类似的东西。然后桌面应用程序对“访问令牌”执行 oauth 身份验证请求。另一个 REST 请求。桌面应用程序接收“访问令牌”和“访问密钥”。

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

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

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

https://i.stack.imgur.com/mlSB7.png

如果您已经对 UI 进行了排序,那么剩下的唯一挑战就是生成 oauth 签名的请求。这会绊倒很多人,因为 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 方法向授予请求令牌(即临时令牌)的服务的 URL 发送一个 oauth 签名的请求。对于 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 是附加了 oauth_token 的“https://api.twitter.com/oauth/authorize?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 事件注册一个处理程序,然后在该处理程序中,获取 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 屏幕抓取。

抓住 pin 后,您不再需要网络浏览器,所以:

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

...您可能还想调用 Dispose() 。

下一步是通过发送另一个 HTTP 消息和该 pin 来获取访问令牌。这是另一个签名的 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”。

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

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

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

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

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

而已。

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

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

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

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

工作示例

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


嗨 Cheeso,感谢您分享您的代码和详细解释。您提供了一个伟大而简单的解决方案。但是,您需要对 GetSignatureBase 方法进行一些小改动以支持非“oob”解决方案。对于非“oob”,您需要对回调进行 URL 编码,因此在迭代 this._params 时需要添加类似的内容: if (p1.Key == "callback") {p.Add( "oauth_" + p1.Key, UrlEncode(p1.Value));继续;}
这不适用于 OAuth 2.0。此类适用于 OAuth 1.0a。 OAuth2.0 使用起来明显更简单,因为没有对各种参数进行签名和字典排序。因此,您可能不需要外部类来执行 OAuth 2.0,或者……如果您确实需要一个外部类,它会比这个简单得多。
在线帮助文件未找到:cheeso.members.winisp.net/OAuthManager1.1
所有链接似乎都已损坏。我在这里找到了一份副本:gist.github.com/DeskSupport/2951522#file-oauth-cs
pastebin.com/eQ0gtTtM 这篇文章中提到的 OAuth.cs 文件的另一个镜像,查看原始链接是如何失效的