2009-08-28 50 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