Способ правильной обработки HttpAntiForgeryException в приложении MVC 4

вот сценарий:

У меня есть страница входа в систему, при входе пользователя он перенаправляется на домашнюю страницу приложения. Затем пользователь использует кнопку возврата браузера, и теперь он находится на странице входа в систему. Он пытается снова войти в систему, но теперь возникает исключение:

HttpAntiForgeryException (0x80004005): предоставленный токен защиты от подделки предназначен для пользователя "", но текущий пользователь-"userName".

Я знаю, что это связано с кэшированием. У меня браузер отключен кэширование для действия входа с помощью настраиваемого фильтра NoCache, который устанавливает все необходимые заголовки-no-cache,no-store, must-revalidate и т. д. Но!--1-->

  • это не работает на всех браузерах
  • особенно Safari (мобильный в большинстве случаев) полностью игнорирует такие настройки

Я попытаюсь сделать хаки и заставить safari mobile обновиться, но это не то, что я ожидаю.

Я хотел бы знать, могу ли я:

  • ручки исключение без показа пользователю любой проблемы существует (полностью прозрачный для пользователя)
  • предотвратите эту проблему, заменив имя пользователя токена Anti forgery что позволит пользователю войти снова без этого исключения, если мои хаки, связанные с кэшированием браузера, перестанут работать в следующих версиях браузеров.
  • Я действительно не хотел бы полагаться на поведение браузера, так как каждый ведет себя по-разному.

обновление 1

чтобы сделать некоторые разъяснения, я знаю, как обрабатывать ошибки в MVC. Проблема в том, что эта обработка ошибок не решает мою проблему вообще. Основная идея обработки ошибок-перенаправление на пользовательскую страницу ошибок с приятным сообщением. Но я хочу предотвратить эту ошибку, а не обрабатывать ее видимым для пользователя способом. Под ручкой я имею в виду catch make username replace или другое подходящее действие, а затем продолжить вход в систему.

обновление 2

Я добавил ниже решение, которое работает для меня.

4 ответов


после некоторого времени исследования я думаю, что нашел способ избавиться от этой ошибки для пользователя. Это не идеально, но, по крайней мере, не отображать страницу ошибки:

Я создал фильтр на основе 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)
{

основная идея этого решения состоит в том, чтобы перенаправить исключение анти-подделки на основное действие индекса. Если пользователь все равно не будет аутентифицирован, он покажетлогин страница если пользователь будет уже аутентифицирован, он покажет индекс


Если у вас есть только одна или несколько функций, создание фильтра может быть немного техническим перебором. Более простое, но не общее решение-просто удалить [ValidateAntiForgeryToken] для конкретного метода и добавление ручной проверки, после проверки, если пользователь вошел в систему.

if (User.Identity.IsAuthenticated)
{
    return RedirectToAction("Index", "Home");
}
System.Web.Helpers.AntiForgery.Validate();
/* proceed with authentication here */

вы должны иметь возможность обрабатывать исключение, добавляя фильтр действий для обработки вашей ошибки.

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

Todo поэтому убедитесь, что пользовательские ошибки включены в вашем интернете.конфиг.

<customErrors mode="On"/>

вы также можете взглянуть на это блог для получения дополнительной информации об ошибке обработки.

редактировать так как вы используете MVC4 и блог о MVC3 вы также можете взглянуть на библиотека MSDN-HandleErrorAttribute, но версия не должна иметь никакого значения.


старый вопрос-но я столкнулся с этой проблемой сегодня, и то, как я ее решил, было перенаправлением на действие выхода:

public ActionResult Login(string returnUrl) 
{
    if (WebSecurity.IsAuthenticated)
        return RedirectToAction("LogOff");

    ...
}