Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

ОБНОВЛЕНИЕ 1

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

ОБНОВЛЕНИЕ 2

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

4b9b3361

Ответ 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 Login:

[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 */

Ответ 3

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

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

Тодо, убедитесь, что пользовательские ошибки включены в ваш web.config.

<customErrors mode="On"/>

Вы также можете посмотреть blog для получения дополнительной информации об ошибке обработки.

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

Ответ 4

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

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

    ...
}