从 WebForm 使用 MVC HtmlHelper [英] Using an MVC HtmlHelper from a WebForm

查看:16
本文介绍了从 WebForm 使用 MVC HtmlHelper的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在向混合 WebForms/MVC 站点添加一些 UI 功能.在本例中,我将一些 AJAX UI 功能添加到 WebForms 页面(通过 jQuery),并且数据来自 MVC JsonResult.一切都在 100% 工作,只有一个例外:

I'm in the process of adding some UI functionality to a hybrid WebForms/MVC site. In this case, I'm adding some AJAX UI features to a WebForms page (via jQuery), and the data is coming from an MVC JsonResult. Everything is working 100%, with one exception:

我想实现 AntiForgeryToken 的 XSRF 保护.我在纯 MVC 应用程序中将它与 ValidateAntiForgeryToken 属性结合使用,但想知道如何在 WebForms 中实现 Html.AntiForgeryToken() 方法.这是一个使用 UrlHelper 的示例.

I would like to implement the XSRF protection of AntiForgeryToken. I have used it in combination with the ValidateAntiForgeryToken attribute on my pure MVC applications, but would like to know how to implement the Html.AntiForgeryToken() method in WebForms. Here's an example using a UrlHelper.

我在正确模拟"ViewContext/RequestContext 时遇到了一些麻烦.我应该如何在 WebForms 页面中使用 HtmlHelpers?

I'm having some trouble getting ViewContext / RequestContext "mocked" up correctly. How should I go about using HtmlHelpers within a WebForms page?

编辑:我希望从我的 WebForms 页面而不是从 MVC JsonResult 中检索 AntiForgeryToken.

Edit: I'm looking to retrieve the AntiForgeryToken from my WebForms page, not from the MVC JsonResult.

推荐答案

关键方法在MVC源码中:GetAntiForgeryTokenAndSetCookie

The key method is in the MVC source code: GetAntiForgeryTokenAndSetCookie

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

This creates an instance of an internal sealed class called AntiForgeryData.

该实例被序列化为 cookie__RequestVerificationToken_"+ 应用程序路径的 base 64 编码版本.

The instance is serialised into a cookie "__RequestVerificationToken_" + a base 64 encoded version of the application path.

AntiForgeryData 的相同实例被序列化为隐藏输入.

The same instance of AntiForgeryData is serialised into a hidden input.

AntiForgeryData 的独特部分是通过 RNGCryptoServiceProvider.GetBytes()

所有这些都可以在 WebForms 页面中进行欺骗,唯一混乱的是隐藏的密封类的序列化.不幸的是,关键方法 (GetAntiForgeryTokenAndSetCookie) 依赖于 ViewContext.HttpContext.Request 来获取 cookie,而 WebForm 需要使用 HttpContext.Current.Request代码>代替.

All of this could be spoofed in a WebForms page, the only messy bit is the serialisation of the hidden sealed class. Unfortunately the key method (GetAntiForgeryTokenAndSetCookie) relies on ViewContext.HttpContext.Request to get at the cookies, while the WebForm needs to use HttpContext.Current.Request instead.

更新

没有太多测试和大量代码,但我想我已经通过一点反思破解了这个问题.在我使用反射的地方,我留下了上面注释掉的等效行:

Not much testing and a lot of code, but I think I've cracked this with a little reflection. Where I've used reflection I've left the equivalent line commented out above:

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()

它创建与 MVC 方法相同的 cookie 和相同的 HTML.

It creates the same cookie and the same HTML as the MVC methods.

我没有为 salt 和 domain 方法烦恼,但它们很容易添加.

I haven't bothered with the salt and domain methods, but they would be fairly easy to add in.

这篇关于从 WebForm 使用 MVC HtmlHelper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆