使用System.Net.Http.HttpClient和MVC提供AntiForgery令牌

时间:2014-12-03 18:02:43

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

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

  

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

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

我试过收集" __ RequestVerificationToken"从一个简单的GET请求,并通过我的POST请求发送它,将它添加到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"交换了。]

谢谢!

2 个答案:

答案 0 :(得分:2)

您可能需要在请求中包含aspnet会话ID cookie

编辑: 好吧,你的权利,它不是会话ID,但你需要两个令牌发送回你的帖子行动。

我认为你做错了是为两个令牌使用相同的值,但它们应该是不同的,同时两个令牌的名称都是__RequestVerificationToken。 从cookie中抓取的令牌应该作为cookie发送回去,从表单字段中抓取的令牌将作为表单字段返回。

答案 1 :(得分:0)

这是因为您从应用程序中错过了POST中HtmlHelper.AntiForgeryToken()的防伪标记。

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