【问题标题】:Provide AntiForgery Token with System.Net.Http.HttpClient and MVC使用 System.Net.Http.HttpClient 和 MVC 提供 AntiForgery 令牌
【发布时间】:2014-12-03 18:02:43
【问题描述】:

我有一个 WPF(我猜可能是任何 winform)应用程序,它尝试使用 HttpClient 登录到标准 MVC 5 网站。

通常我可以通过调用 PostAsync() 成功登录,我在 HttpContent 中提供用户名和密码参数!

但是,当我将 [ValidateAntiForgeryToken] 添加到控制器的登录 (POST) 操作时,PostAsync() 调用失败并出现内部服务器错误。

我尝试从一个简单的 GET 请求中收集“__RequestVerificationToken”,并通过将其添加到 POST 参数、请求的标头或 HttpHandler 的 CookieContainer(或任何三者的组合)但我仍然从服务器收到错误 500。

我知道可以使用 HttpWebRequests (apparently) 来完成,但我不知道使用 HttpClient 时缺少什么。我也不知道服务器端到底出了什么问题。或者如何检查,因为代码永远不会到达我的控制器方法。

有没有其他人尝试过这个?

编辑 1

我正在为 GET 和 POST 添加浏览器发送的原始数据:

GET http://localhost:57457/Account/Login HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:57457/Account/Login
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
DNT: 1
Host: localhost:57457
Cookie: NavigationTreeViewState=%5b%7b%27N0_1%27%3a%27T%27%2c%27N0%27%3a%27T%27%7d%2c%27N0_1_2%27%2c%7b%7d%5d; style=default; __RequestVerificationToken=Bak42Ga5sHJitYlmut6OgvmqXNmP7kKQRNaMSsLMAUh86iHGGmz5pnNfz_soKu46Wax9sG23arPOTnSh1bvaWyWqQ9NH4GJxFmendW8VFTg1

RESPONSE:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 5.2
X-Frame-Options: SAMEORIGIN
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRUJTLkNvZGVcUHJvamVjdHNcQ1ZSUE9TX1dlYlNpdGVcQ1ZSUE9TX1dlYlNpdGVcQWNjb3VudFxMb2dpbg==?=
X-Powered-By: ASP.NET
Date: Thu, 04 Dec 2014 10:00:00 GMT
Content-Length: 1734
[View page content]

POST http://localhost:57457/Account/Login HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:57457/Account/Login
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Content-Length: 180
DNT: 1
Host: localhost:57457
Pragma: no-cache
Cookie: NavigationTreeViewState=%5b%7b%27N0_1%27%3a%27T%27%2c%27N0%27%3a%27T%27%7d%2c%27N0_1_2%27%2c%7b%7d%5d; style=default; __RequestVerificationToken=Bak42Ga5sHJitYlmut6OgvmqXNmP7kKQRNaMSsLMAUh86iHGGmz5pnNfz_soKu46Wax9sG23arPOTnSh1bvaWyWqQ9NH4GJxFmendW8VFTg1

__RequestVerificationToken=Bak42Ga5sHJitYlmut6OgvmqXNmP7kKQRNaMSsLMAUh86iHGGmz5pnNfz_soKu46Wax9sG23arPOTnSh1bvaWyWqQ9NH4GJxFmendW8VFTg1&UserName=test&Password=test

RESPONSE:
HTTP/1.1 400 Bad request (user/password for testing purposes only)
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 5.2
X-Frame-Options: SAMEORIGIN
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRUJTLkNvZGVcUHJvamVjdHNcQ1ZSUE9TX1dlYlNpdGVcQ1ZSUE9TX1dlYlNpdGVcQWNjb3VudFxMb2dpbg==?=
X-Powered-By: ASP.NET
Date: Thu, 04 Dec 2014 10:00:00 GMT
Content-Length: 4434
[View page content]

编辑 2

这是我的应用为 GET 和 POST 发送的内容:

GET http://localhost:57457/Account/Login HTTP/1.1
Host: localhost:57457
Connection: Keep-Alive

POST http://localhost:57457/Account/Login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:57457
Cookie: __RequestVerificationToken=df9nBSP_J1IiLrv84RwrkmvbYBrnH4iqv97wRvz6HMPLWBhgI4XzGeAFcschovHwD8mTtHU6xrmVxz1Ku96_BaoB79le_vLTcrgGemU4gjc1
Content-Length: 163
Expect: 100-continue

__RequestVerificationToken=df9nBSP_J1IiLrv84RwrkmvbYBrnH4iqv97wRvz6HMPLWBhgI4XzGeAFcschovHwD8mTtHU6xrmVxz1Ku96_BaoB79le_vLTcrgGemU4gjc1&UserName=test&Password=test

最后这是错误:

[HttpAntiForgeryException (0x80004005):验证提供的防伪令牌失败。 cookie“__RequestVerificationToken”和表单域“__RequestVerificationToken”被交换了。]

谢谢!

【问题讨论】:

  • 您能否也发布原始 http 响应
  • 只是标题顺便说一句

标签: c# asp.net-mvc winforms asp.net-mvc-4 winforms-to-web


【解决方案1】:

您可能需要在请求中包含 aspnet session id cookie

编辑: 好的,没错,这不是会话 ID,但您需要两个令牌才能将其发送回您的发布操作。

我认为您做错的事情是对两个令牌使用相同的值,但它们应该不同,尽管两个令牌的名称都是 __RequestVerificationToken。 从 cookie 中抓取的令牌应该作为 cookie 发送回来,从表单字段中抓取的令牌作为表单字段返回。

【讨论】:

  • 谢谢,但我不这么认为。这样做真的没有任何意义,此外,我在没有 [ValidateAntiForgeryToken] 的情况下登录时不会传递任何会话 ID,它工作得很好。
  • 据我所知,生成的伪造令牌绑定到一个会话值,出于安全原因,该会话值保留在服务器上,因此请求令牌并在之后进行验证,如登录期间,应该发生在同一个会话中.
  • 就服务器而言,一切都发生在同一个会话中。话虽如此,我绝对怀疑会话与此有关(您可以从我的编辑中看到浏览器正在使用哪些数据/cookie)
  • 是的,就是这样!有两个令牌值,我愚蠢地只为两者发送了 cookie 值。现在这个错误确实很有意义:)。谢谢一百万!
【解决方案2】:

这是因为您在应用程序的 POST 中缺少来自 HtmlHelper.AntiForgeryToken() 的防伪令牌。

您需要从您的 WPF 应用程序加载一个页面,并在视图上显示 HtmlHelper.AntiForgeryToken()。然后取名为 __RequestVerificationToken 的隐藏输入元素的值,并将其附加到您对服务器的登录 POST 请求中。

【讨论】:

  • 对不起,我在我的问题中遗漏了该信息,但我也尝试过:我提出了一个 GET 请求,找出了 __RequestVerificationToken 并将其添加到我能想到的所有地方:帖子参数(模拟隐藏的浏览器字段)、请求标头(因为我看到浏览器也这样做)和 cookie(因为我的第一个 GET 请求还是把它放在那里)。遗憾的是,它们在任何组合中都不起作用。
  • @raptor 您是否尝试过使用 Fiddler 之类的工具来检查真实的登录请求,然后查看您的应用正在提交什么?
  • 非常抱歉,我不知道如何使用 fiddler 来监控应用程序。当我使用该应用程序登录时,它不显示任何流量。现在,我在问题中添加了从浏览器获取的数据,我将研究如何(或是否)可以使用 fiddler 实际监控应用程序并返回我的发现。
  • 经过多次痛苦的尝试后,我设法使用了 fiddler(可用于 IIS 交互的文档大多具有误导性)。我将结果添加到我的问题中。一件好事是我看到了实际的错误。也许现在我可以弄清楚这意味着什么:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-06
  • 1970-01-01
  • 2012-04-10
  • 1970-01-01
相关资源
最近更新 更多