.NET 中带验证的 OAuth

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4002847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 14:50:44  来源:igfitidea点击:

OAuth with Verification in .NET

.netoauthmendeley

提问by John

I'm trying to create a .NET-based client app (in WPF - although for the time being I'm just doing it as a console app) to integrate with an OAuth-enabled application, specifically Mendeley (http://dev.mendeley.com), which apparently uses 3-legged OAuth.

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

This is my first time using OAuth, and I'm having a lot of difficulty getting started with it. I've found several .NET OAuth libraries or helpers, but they seem to be more complicated than I think I need. All I want to do is be able to issue REST requests to the Mendeley API and get responses back!

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

So far, I've tried:

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

The first (DotNetOpenAuth) seems like it could possibly do what I needed if I spent hours and hours trying to work out how. The second and third, as best I can tell, don't support the verification codes that Mendeley is sending back -- although I could be wrong about this :)

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

I've got a consumer key and secret from Mendeley, and with DotNetOpenAuth I managed to get a browser launched with the Mendeley page providing a verification code for the user to enter into the application. However, at this point I got lost and couldn't work out how to sensibly provide that back to the application.

我从 Mendeley 那里获得了一个消费者密钥和秘密,并且通过 DotNetOpenAuth 我设法启动了一个浏览器,Mendeley 页面提供了一个验证码供用户进入应用程序。但是,此时我迷路了,无法弄清楚如何明智地将其提供给应用程序。

I'm very willing to admit that I have no idea where to start with this (although it seems like there's quite a steep learning curve) - if anyone can point me in the right direction I'd appreciate it!

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

回答by Cheeso

I agree with you. The open-source OAuth support classes available for .NET apps are hard to understand, overly complicated (how many methods are exposed by DotNetOpenAuth?), poorly designed (look at the methods with 10 string parameters in the OAuthBase.cs module from that google link you provided - there's no state management at all), or otherwise unsatisfactory.

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

It doesn't need to be this complicated.

没必要这么复杂。

I'm not an expert on OAuth, but I have produced an OAuth client-side manager class, that I use successfully with Twitter and TwitPic. It's relatively simple to use. It's open source and available here: Oauth.cs

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

For review, in OAuth 1.0a...kinda funny, there's a special name and it looks like a "standard" but as far as I know the only service that implements "OAuth 1.0a" is Twitter. I guess that's standard enough. ok, anyway in OAuth 1.0a, the way it works for desktop appsis this:

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

  1. You, the developer of the app, register the app and get a "consumer key" and "consumer secret". On Arstechnica, there's a well written analysis of why this model isn't the best, but as they say, it is what it is.

  2. Your app runs. The first time it runs, it needs to get the user to explicitly grant approval for the app to make oauth-authenticated REST requests to Twitter and its sister services (like TwitPic). To do this you must go through an approval process, involving explicit approval by the user. This happens only the first time the app runs. Like this:

    • request a "request token". Aka temporary token.
    • pop a web page, passing that request token as a query param. This web page presents UI to the user, asking "do you want to grant access to this app?"
    • the user logs in to the twitter web page, and grants or denies access.
    • the response html page appears. If the user has granted access, there's a PIN displayed in a 48-pt font
    • the user now needs to cut/paste that pin into a windows form box, and click "Next" or something similar.
    • the desktop app then does an oauth-authenticated request for an "Access token". Another REST request.
    • the desktop app receives the "access token" and "access secret".
  1. 您,应用程序的开发者,注册应用程序并获得“消费者密钥”和“消费者秘密”。在 Arstechnica 上,有一篇写得很好的分析为什么这个模型不是最好的,但正如他们所说,它就是这样

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

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

After the approval dance, the desktop app can just use the user-specific "access token" and "access secret" (along with the app-specific "consumer key" and "consumer secret") to do authenticated requests on behalf of the user to Twitter. These don't expire, although if the user de-authorizes the app, or if Twitter for some reason de-authorizes your app, or if you lose your access token and/or secret, you'd need to do the approval dance again.

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



If you're not clever, the UI flow can sort of mirror the multi-step OAuth message flow. There is a better way.

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

Use a WebBrowser control, and open the authorize web page within the desktop app. When the user clicks "Allow", grab the response text from that WebBrowser control, extract the PIN automatically, then get the access tokens. You send 5 or 6 HTTP requests but the user needs to see only a single Allow/Deny dialog. Simple.

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

Like this:
alt text

像这样:
替代文字



If you've got the UI sorted, the only challenge that remains is to produce oauth-signed requests. This trips up lots of people because the oauth signing requirements are sort of particular. That's what the simplified OAuth Manager class does.

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

Example code to request a token:

请求令牌的示例代码:

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. Simple. As you can see from the code, the way to get to oauth parameters is via a string-based indexer, something like a dictionary. The AcquireRequestToken method sends an oauth-signed request to the URL of the service that grants request tokens, aka temporary tokens. For Twitter, this URL is "https://api.twitter.com/oauth/request_token". The oauth spec says you need to pack up the set of oauth parameters (token, token_secret, nonce, timestamp, consumer_key, version, and callback), in a certain way (url-encoded and joined by ampersands), and in a lexicographically-sorted order, generate a signature on that result, then pack up those same parameters along with the signature, stored in the new oauth_signature parameter, in a different way (joined by commas). The OAuth manager class does this for you automatically.It generates nonces and timestamps and versions and signaturesautomatically - your app doesn't need to care or be aware of that stuff. Just set the oauth parameter values and make a simple method call. the manager class sends out the request and parses the response for you.

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

Ok, then what? Once you get the request token, you pop the web browser UI in which the user will explicitly grant approval. If you do it right, you'll pop this in an embedded browser. For Twitter, the URL for this is "https://api.twitter.com/oauth/authorize?oauth_token=" with the oauth_token appended. Do this in code like so:

好的,然后呢?获得请求令牌后,您会弹出 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);

(If you were doing this in an external browser you'd use System.Diagnostics.Process.Start(url).)

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

Setting the Url property causes the WebBrowser control to navigate to that page automatically.

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

When the user clicks the "Allow" button a new page will be loaded. It's an HTML form and it works the same as in a full browser. In your code, register a handler for the DocumentedCompleted event of the WebBrowser control, and in that handler, grab the pin:

当用户单击“允许”按钮时,将加载一个新页面。它是一个 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]+).*", "").Trim();

That's a bit of HTML screen scraping.

这有点像 HTML 屏幕抓取。

After grabbing the pin, you don't need the web browser any more, so:

抓住大头针后,您不再需要网络浏览器,因此:

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

...and you might want to call Dispose() on it as well.

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

The next step is getting the access token, by sending another HTTP message along with that pin. This is another signed oauth call, constructed with the oauth ordering and formatting I described above. But once again this is really simple with the OAuth.Manager class:

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

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

For Twitter, that URL is "https://api.twitter.com/oauth/access_token".

对于 Twitter,该 URL 是“ https://api.twitter.com/oauth/access_token”。

Now you have access tokens, and you can use them in signed HTTP requests. Like this:

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

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

...where urlis the resource endpoint. To update the user's status, it would be "http://api.twitter.com/1/statuses/update.xml?status=Hello".

...url资源端点在哪里。要更新用户的状态,它将是“ http://api.twitter.com/1/statuses/update.xml?status=Hello”。

Then set that string into the HTTP Header named Authorization.

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

To interact with third-party services, like TwitPic, you need to construct a slightly differentOAuth header, like this:

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

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

For Twitter, the values for the verify creds url and realm are "https://api.twitter.com/1/account/verify_credentials.json", and "http://api.twitter.com/" respectively.

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

...and put thatauthorization string in an HTTP header called X-Verify-Credentials-Authorization. Then send that to your service, like TwitPic, along with whatever request you're sending.

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

That's it.

就是这样。

All together, the code to update twitter status might be something like this:

总之,更新 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 is sort of complicated under the covers, but using it doesn't need to be. The OAuth.Manager handles the generation of outgoing oauth requests, and the receiving and processing of oauth content in the responses. When the Request_token request gives you an oauth_token, your app doesn't need to store it. The Oauth.Manager is smart enough to do that automatically. Likewise when the access_token request gets back an access token and secret, you don't need to explicitly store those. The OAuth.Manager handles that state for you.

OAuth 1.0a 在幕后有点复杂,但使用它并不需要如此。OAuth.Manager 处理传出 oauth 请求的生成,以及响应中 oauth 内容的接收和处理。当 Request_token 请求为您提供 oauth_token 时,您的应用程序不需要存储它。Oauth.Manager 足够智能,可以自动执行此操作。同样,当 access_token 请求取回访问令牌和机密时,您不需要显式存储它们。OAuth.Manager 为您处理该状态。

In subsequent runs, when you already have the access token and secret, you can instantiate the OAuth.Manager like this:

在后续运行中,当您已经拥有访问令牌和密码时,您可以像这样实例化 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;

...and then generate authorization headers as above.

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

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

You can download a DLL containing the OAuth.Manager class here. There is also a helpfile in that download. Or you can view the helpfile online.

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

See an example of a Windows Form that uses this manager here.

在此处查看使用此管理器的 Windows 窗体示例。



WORKING EXAMPLE

工作实例

Download a working exampleof a command-line tool that uses the class and technique described here:

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