2009-08-28 51 views
17

我正在向混合WebForms/MVC网站添加一些UI功能。在这种情况下,我将一些AJAX UI功能添加到WebForms页面(通过jQuery),并且数据来自MVC JsonResult。一切工作都是100%,但有一个例外:使用WebForm中的MVC HtmlHelper

我想实施AntiForgeryToken的XSRF保护。我已将它与纯粹的MVC应用程序中的ValidateAntiForgeryToken属性结合使用,但想知道如何在WebForms中实现Html.AntiForgeryToken()方法。 Here's an example using a UrlHelper

我有一些麻烦得到ViewContext/RequestContext“嘲笑”正确。我应该如何在WebForms页面中使用HtmlHelpers?

编辑: 我期待从我的WebForms页面检索AntiForgeryToken,而不是从MVC JsonResult。

+0

我有同样的问题 - 一个传统的WebForms页面,需要发布到一个MVC动作与'AntiForgeryToken'。我想将'Html.AntiForgeryToken()'添加到WebForms页面,而不用在MVC中重写它。 – Keith 2010-03-31 13:22:26

回答

6

的关键方法是在MVC源代码:GetAntiForgeryTokenAndSetCookie

这将创建一个名为AntiForgeryData内部密封类的一个实例。

将实例序列化为cookie“__RequestVerificationToken_”+应用程序路径的基础64编码版本。

AntiForgeryData的同一个实例被序列化为一个隐藏的输入。

AntiForgeryData的独特之处是使用了一个RNGCryptoServiceProvider.GetBytes()

所有这一切都可以在一个页面的WebForms是伪造的,唯一的凌乱位是隐藏密封类的序列化。不幸的是,关键方法(GetAntiForgeryTokenAndSetCookie)依赖于ViewContext.HttpContext.Request来获取cookie,而WebForm需要使用HttpContext.Current.Request来代替。


更新

没有太多的测试和大量的代码,但我想我已经有一点思考破解这个。当我使用反射我已经离开上述注释掉相当于行:

using System; 
using System.Reflection; 
using System.Web; 
using System.Web.Mvc; 

/// <summary>Utility to provide MVC anti forgery tokens in WebForms pages</summary> 
public class WebFormAntiForgery 
{ 
    /// <summary>Create an anti forgery token in a WebForms page</summary> 
    /// <returns>The HTML input and sets the cookie</returns> 
    public static string AntiForgeryToken() 
    { 
     string formValue = GetAntiForgeryTokenAndSetCookie(); 

     // string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null); 
     var mvcAssembly = typeof(HtmlHelper).Assembly; 
     var afdType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryData"); 
     string fieldName = Convert.ToString(afdType.InvokeMember(
      "GetAntiForgeryTokenName", 
      BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, 
      null, 
      null, 
      new object[] { null })); 

     TagBuilder builder = new TagBuilder("input"); 
     builder.Attributes["type"] = "hidden"; 
     builder.Attributes["name"] = fieldName; 
     builder.Attributes["value"] = formValue; 
     return builder.ToString(TagRenderMode.SelfClosing); 
    } 

    static string GetAntiForgeryTokenAndSetCookie() 
    { 
     var mvcAssembly = typeof(HtmlHelper).Assembly; 
     var afdType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryData"); 

     // new AntiForgeryDataSerializer(); 
     var serializerType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryDataSerializer"); 
     var serializerCtor = serializerType.GetConstructor(new Type[0]); 
     object serializer = serializerCtor.Invoke(new object[0]); 

     // string cookieName = AntiForgeryData.GetAntiForgeryTokenName(HttpContext.Current.Request.ApplicationPath); 
     string cookieName = Convert.ToString(afdType.InvokeMember(
      "GetAntiForgeryTokenName", 
      BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, 
      null, 
      null, 
      new object[] { HttpContext.Current.Request.ApplicationPath })); 

     // AntiForgeryData cookieToken; 
     object cookieToken; 
     HttpCookie cookie = HttpContext.Current.Request.Cookies[cookieName]; 
     if (cookie != null) 
     { 
      // cookieToken = Serializer.Deserialize(cookie.Value); 
      cookieToken = serializerType.InvokeMember("Deserialize", BindingFlags.InvokeMethod, null, serializer, new object[] { cookie.Value }); 
     } 
     else 
     { 
      // cookieToken = AntiForgeryData.NewToken(); 
      cookieToken = afdType.InvokeMember(
       "NewToken", 
       BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, 
       null, 
       null, 
       new object[0]); 

      // string cookieValue = Serializer.Serialize(cookieToken); 
      string cookieValue = Convert.ToString(serializerType.InvokeMember("Serialize", BindingFlags.InvokeMethod, null, serializer, new object[] { cookieToken })); 

      var newCookie = new HttpCookie(cookieName, cookieValue) { HttpOnly = true }; 

      HttpContext.Current.Response.Cookies.Set(newCookie); 
     } 

     // AntiForgeryData formToken = new AntiForgeryData(cookieToken) 
     // { 
     //  CreationDate = DateTime.Now, 
     //  Salt = salt 
     // }; 
     var ctor = afdType.GetConstructor(new Type[] { afdType }); 
     object formToken = ctor.Invoke(new object[] { cookieToken }); 

     afdType.InvokeMember("CreationDate", BindingFlags.SetProperty, null, formToken, new object[] { DateTime.Now }); 
     afdType.InvokeMember("Salt", BindingFlags.SetProperty, null, formToken, new object[] { null }); 

     // string formValue = Serializer.Serialize(formToken); 
     string formValue = Convert.ToString(serializerType.InvokeMember("Serialize", BindingFlags.InvokeMethod, null, serializer, new object[] { formToken })); 
     return formValue; 
    } 
} 

用法,然后类似MVC:

WebFormAntiForgery.AntiForgeryToken() 

它创建相同的Cookie和相同的HTML作为MVC方法。

我并没有打扰盐和域的方法,但他们会很容易添加。

+0

嗨,我只是试图在webforms应用程序(.NET 4.0,MVC 3.0),并从此行中获得“未设置为对象实例的对象引用”错误: var serializerCtor = serializerType.GetConstructor(new Type [0]); 有人可以帮忙吗?我在这里有点偏离我的深度。 – cjacques 2011-03-10 07:14:13

+0

@BFOT这是写在ASP.Net 2,MVC 1,是一个彻头彻尾的黑客 - 我反映出他们的反伪造令牌的东西,但它都是私有方法。有可能这需要对每个新版本的ASP和MVC进行一些更新,下载MVC 3源代码并查看这些方法是如何改变的 - 如果我们非常幸运,他们甚至可能已经暴露了它们,以便WebForms可以使用它们。 – Keith 2011-03-10 09:47:22

+0

感谢您的回复。我将在本周末看看最新的MVC消息来源,看看我能否解决这个问题。如果有空闲时间,请随时发布上述代码的修订版本。 ;) – cjacques 2011-03-11 00:59:26

0

您可以创建在您的控制器一个新的HtmlHelper,然后从那里拉抗xrsf:

var htmlHelper = new HtmlHelper(
    new ViewContext(
     ControllerContext, 
     new WebFormView("omg"), 
     new ViewDataDictionary(), 
     new TempDataDictionary()), 
     new ViewPage()); 

var xsrf = htmlHeler.AntiForgeryToken; 

myObject.XsrfToken = xsrf; 

return JsonResult(myObject); 
+0

这是我的理解,AntiForgeryToken设置一个cookie,并注入一个隐藏的表单域,所以两者可以进行比较。这是如何实现的? – 2009-08-28 18:15:21

1

默认情况下,ASP.NET Web窗体已经包括的措施来验证事件和视图状态。 Phil Haack在链接的文章中谈到了一点。 XSRF减缓战略谈到here (Scott Hanselman)here (Dino Esposito)

+1

非常好的链接,谢谢。我希望能够专门使用AntiForgeryToken,因为MVC Web服务的“消费者”位于WebForms页面(我不会在MVC中重写)。 – 2009-09-08 00:31:02

17

我知道这是一个古老的问题,但今天我遇到了这个问题,并认为我会分享。我正在使用MVC4,并且在两个MVC(通过RenderPartial)和WebForms之间共享一个webform控件(.ascx)。在那个控制中,我需要一个防伪标记。幸运的是,有一个助手,现在你可以在你的web表单现在用的这如此简单:

<%= AntiForgery.GetHtml() %> 

,这会使得你的防伪标记,就像您在MVC获得。

Here's the MS link to it

+0

非常好,这是解决WebForms 2/MVC 3及更高版本中问题的更好方法。 – Keith 2013-05-07 10:55:57