在 MVC 4 应用程序中正确处理 HttpAntiForgeryException 的方法

Posted

技术标签:

【中文标题】在 MVC 4 应用程序中正确处理 HttpAntiForgeryException 的方法【英文标题】:A way of properly handling HttpAntiForgeryException in MVC 4 application 【发布时间】:2012-10-09 16:39:45 【问题描述】:

这是场景:

我有一个登录页面,当用户签名时,它被重定向到主页应用程序页面。然后用户正在使用浏览器后退按钮,现在他在登录页面上。他尝试再次登录,但现在抛出异常:

HttpAntiForgeryException (0x80004005):提供的防伪令牌用于用户“”,但当前用户是“用户名”。

我知道这与缓存有关。我使用自定义 NoCache 过滤器禁用了登录操作的浏览器缓存,该过滤器设置了所有必需的标头 - 无缓存、无存储、必须重新验证等。但是

这不适用于所有浏览器 尤其是 Safari(大多数情况下是移动设备)完全忽略此类设置

我会尝试破解并强制 safari mobile 刷新,但这不是我所期望的。

我想知道我是否可以:

处理异常而不向用户显示任何问题存在(对用户完全透明) 通过替换防伪令牌用户名来防止此问题,如果我的与浏览器缓存相关的黑客在下一版本的浏览器中停止工作,这将允许用户再次登录而不会出现此异常。 我真的不想依赖浏览器的行为,因为每个浏览器的行为都不同。

更新 1

为了澄清一下,我知道如何处理 MVC 中的错误。问题是这种处理错误根本没有解决我的问题。错误处理的基本思想是重定向到带有好消息的自定义错误页面。但我想防止这个错误发生,而不是以用户可见的方式处理它。通过句柄,我的意思是捕获使用户名替换或其他合适的操作,然后继续登录。

更新 2

我添加了以下对我有用的解决方案。

【问题讨论】:

我完全同意您在问题和答案中的澄清更新。 Login-Logic 不应依赖空尝试捕获、重定向到错误页面、重新加载页面(要求用户再次登录 - 输入其凭据后立即)或破坏以其他用户身份登录时(而其他用户当前仍处于登录状态)。 【参考方案1】:

经过一段时间的调查,我想我找到了一些方法来为用户摆脱这个错误。它并不完美但至少不显示错误页面:

我根据HandleErrorAttribute创建了过滤器:

    [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", 
        Justification = "This attribute is AllowMultiple = true and users might want to override behavior.")]
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class LoginAntiforgeryHandleErrorAttribute : FilterAttribute, IExceptionFilter
    
        #region Implemented Interfaces

        #region IExceptionFilter

        /// <summary>
        /// </summary>
        /// <param name="filterContext">
        /// The filter context.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public virtual void OnException(ExceptionContext filterContext)
        
            if (filterContext == null)
            
                throw new ArgumentNullException("filterContext");
            

            if (filterContext.IsChildAction)
            
                return;
            

            // If custom errors are disabled, we need to let the normal ASP.NET exception handler
            // execute so that the user can see useful debugging information.
            if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
            
                return;
            

            Exception exception = filterContext.Exception;

            // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
            // ignore it.
            if (new HttpException(null, exception).GetHttpCode() != 500)
            
                return;
            

            // check if antiforgery
            if (!(exception is HttpAntiForgeryException))
            
                return;
            

            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary
                
                     "action", "Index" , 
                     "controller", "Home" 
                );

            filterContext.ExceptionHandled = true;
        

        #endregion

        #endregion
    

然后我将此过滤器应用于登录 POST 操作:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[LoginAntiforgeryHandleError]
public ActionResult Login(Login model, string returnUrl)

此解决方案的主要思想是将防伪异常重定向到主索引操作。如果用户仍然不会未经身份验证,它将显示 login 页面如果用户已经通过身份验证,它将显示 index 页面。

更新 1 此解决方案存在一个潜在问题。如果有人使用不同的凭据登录,那么在出错时应该添加额外的登录运行时 - 注销以前的用户并登录新用户。不处理这种情况。

【讨论】:

这对我很有用。我不需要它做任何幻想。只是不要抛出黄屏。【参考方案2】:

如果您只有一个或几个功能受到影响,那么创建过滤器在技术上可能有点矫枉过正。一个更简单但非通用的解决方案是简单地删除特定方法的[ValidateAntiForgeryToken],并在检查用户是否登录后添加手动验证。

if (User.Identity.IsAuthenticated)

    return RedirectToAction("Index", "Home");

System.Web.Helpers.AntiForgery.Validate();
/* proceed with authentication here */

【讨论】:

谢谢,这似乎工作得很好,仍然提供相同的安全性。 这对我不起作用,它给出了完全相同的错误,但在这一行:System.Web.Helpers.AntiForgery.Validate(); @AmrElgarhy,现在你可以抓住它了!【参考方案3】:

您应该能够通过添加操作过滤器来处理您的错误来处理异常。

[HandleError(View="AntiForgeryExceptionView", ExceptionType = typeof(HttpAntiForgeryException))]

请确保在您的 web.config 中打开自定义错误。

<customErrors mode="On"/>

您还可以查看此blog,了解有关处理错误的更多信息。

编辑由于您使用的是 MVC4 并且博客是关于 MVC3 的,您还可以查看 MSDN library - HandleErrorAttribute,但版本应该不会有什么不同。

【讨论】:

是的,但这一切都说明了重定向(因此处理此错误会破坏当前操作(执行)。我想在不破坏操作的情况下处理此错误,因此在这种情况下处理,替换用户名并继续使用正确的数据登录 我认为,当您可以捕获错误时,您可以将其重定向到一个视图(控制器),您可以在其中替换用户名并使用先前输入的数据继续登录。另见:***.com/questions/1794936/…【参考方案4】:

一个老问题 - 但我今天遇到了这个问题,我解决的方法是重定向到注销操作,如下所示:

public ActionResult Login(string returnUrl) 

    if (WebSecurity.IsAuthenticated)
        return RedirectToAction("LogOff");

    ...

【讨论】:

【参考方案5】:

在您之前进行身份验证后,登录时会显示该消息。 复制步骤: 1.) 打开您的登录页面并验证您未通过身份验证。 2.) 复制选项卡并在第二个选项卡上登录。 3.) 返回第一个选项卡并尝试登录(无需重新加载页面)。 4.) 你看到这个错误;如果您的登录操作装饰有 [ValidateAntiForgeryToken]属性:

System.Web.Mvc.HttpAntiForgeryException: 提供的防伪令牌适用于用户“”, 但当前用户是“YourUserNameOrEmailAddress”。

此 Helper 执行与 [ValidateAntiForgeryToken] 属性相同的验证:

System.Web.Helpers.AntiForgery.Validate()

从 Login Action 中删除 [ValidateAntiForgeryToken] 并改用此方法。

现在,当用户已经通过身份验证时,它将重定向到主页。 如果已通过身份验证,但以其他人身份登录,则注销当前用户并在以新用户身份进行身份验证之前继续验证防伪令牌。

if (User.Identity.IsAuthenticated)

    if (User.Identity.Name == UserName)//User is already Logged in.
        return RedirectToAction("Index", "Home");
    else//Else: User is Logging In as someone else, so Log Out the Current User.
        ResetUser();

System.Web.Helpers.AntiForgery.Validate();//Replaces [ValidateAntiForgeryToken].
//Add your Login Logic below here.

然后,添加此功能以安全地重置用户,而无需再次重新加载页面:

private void ResetUser()

    //Add any additional custom Sign-Out/Log-Off Logic here.
    Session.Abandon();
    FormsAuthentication.SignOut();

    //Source: https://***.com/questions/4050925/page-user-identity-isauthenticated-still-true-after-formsauthentication-signout
    //The User.Identity is Read-Only, but it reads from HttpContext.User, which we may Reset.  Otherwise it will still show as Authenticated until the next Page Load.
    HttpContext.User = new System.Security.Principal.GenericPrincipal(new System.Security.Principal.GenericIdentity(string.Empty), null);//Do not set Identity to null, because other parts of the code may assume it's blank.

.net 核心思想: 我应该注意,如果您使用的是 .net Core 并且在您的控制器上有 [AutoValidateAntiforgeryToken] 属性 - 或者您已经向整个站点添加了一个全局过滤器,如 services.AddMvc(options =&gt; options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); ); - 那么您可以选择装饰您的登录 -带有[IgnoreAntiforgeryToken] 的操作方法可避免自动验证异常,并让您有机会在继续并自己手动调用验证辅助方法之前重定向或注销。 注意:我还没有使用 .net Core 来验证这一点,但在此处添加我的发现以防万一。

【讨论】:

以上是关于在 MVC 4 应用程序中正确处理 HttpAntiForgeryException 的方法的主要内容,如果未能解决你的问题,请参考以下文章

ASP.Net MVC 3/4 托管在 IIS 7.5 默认处理程序映射上

如何在 .NET MVC 2 中实现正确的 HTTP 错误处理?

在 MVC 中处理 $_POST 数据的正确方法是啥?

两个 servlet 中的 Spring、MVC 和 REST

ASP.NET MVC 4 - 以 HTML、JSON 或 XML 响应的正确方法

如何正确处理 ASP.NET MVC 中的 404?