修补Asp.net Mvc2 AntiForgeryToken异常

时间:2010-09-28 15:35:09

标签: asp.net-mvc asp.net-mvc-2 reflection patch antiforgerytoken

我的问题的一些背景:

Mvc2似乎有ValidateAntiForgeryTokenAttribute的变更/错误。

从Mvc1升级到Mvc2时,具有活动会话的用户在使用ValidateAntiForgeryTokenAttribute请求页面时会收到以下错误:

  

无法将'System.Web.UI.Triplet'类型的对象强制转换为'System.Object []'。

问题记录在案here

升级到Mvc2后,我们预计会受到此问题的严重影响。我编写了一个从注释中的代码派生的修复程序(下面记录为后代)。目前,通过创建ControllerAsyncController的子类来覆盖Initialize方法来调用此代码,以便纠正问题。 e.g。

public class FixedController:Controller
{
    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        this.FixAntiForgeryTokenMvc1ToMvc2(requestContext); //extension
    }
}

internal static class ControllerEx
{
    public static void FixAntiForgeryTokenMvc1ToMvc2(
        this Controller controller,
        RequestContext requestContext)
    {
        var cc = new ControllerContext(requestContext,
                                       controller);
        var antiForgeryAttribute = new ValidateAntiForgeryTokenAttribute();
        try
        {
            antiForgeryAttribute.OnAuthorization(new AuthorizationContext(cc));
        }
        catch (HttpAntiForgeryException forgeryException)
        {
            var castException = forgeryException.InnerException;
            if (castException != null
                && castException is InvalidCastException
                && castException.Message.StartsWith(
                       "Unable to cast object of type"
                       + " 'System.Web.UI.Triplet' to type"
                       + " 'System.Object[]'"))
            {
                var responseTokenCookieNames =
                    controller
                        .Response
                        .Cookies
                        .Cast<Cookie>()
                        .Select(c => c.Name)
                        .Where(n => n.Contains("RequestVerificationToken"));
                foreach (var s in responseTokenCookieNames)
                {
                    var cookie = controller.Response.Cookies[s];
                    if (cookie != null)
                    {
                        cookie.Value = "";
                    }
                }
                var requestTokenCookieNames =
                    controller
                        .Request
                        .Cookies
                        .Cast<String>()
                        .Where(n => n.Contains("RequestVerificationToken"))
                        .ToList();
                foreach (var c in requestTokenCookieNames)
                {
                    controller.Request.Cookies.Remove(c);
                }
            }
        }
    }
}

这样做的结果是我必须改变我的所有控制器类,以从我的新的,更正的Controller子类派生。对于打算在大约一个月的时间内弃用的代码,这似乎非常具有侵扰性。

所以,回答我的问题,我想知道是否有一种较少侵入性的修补现有类的方法,以便不必改变类的下游用户,也许使用反射?

1 个答案:

答案 0 :(得分:1)

彭德,

而不是编码每个控制器,使用基本控制器并继承它:

public abstract class BaseController : Controller
{
    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        FixAntiForgeryTokenMvc1ToMvc2(this, requestContext);
    }
    private static void FixAntiForgeryTokenMvc1ToMvc2(
        Controller controller, RequestContext requestContext)
    {
        // stuff ....
    }
}

在你的'普通'控制器中,只需:

public class NormalController : BaseController
{
    // all the previous stuff
}

试一试......