web-dev-qa-db-fra.com

Un moyen de gérer correctement HttpAntiForgeryException dans l'application MVC 4

Voici le scénario:

J'ai une page de connexion, lorsque l'utilisateur la signe, elle est redirigée vers la page d'application d'accueil. Ensuite, l'utilisateur utilise le bouton de retour du navigateur, et maintenant il est sur la page de connexion. Il essaie à nouveau de se connecter mais maintenant une exception est levée:

HttpAntiForgeryException (0x80004005): le jeton anti-contrefaçon fourni était destiné à l'utilisateur "", mais l'utilisateur actuel est "userName".

Je sais que cela est lié à la mise en cache. J'ai désactivé la mise en cache du navigateur pour l'action de connexion en utilisant un filtre NoCache personnalisé qui définit tous les en-têtes requis - pas de cache, pas de magasin, doit revalider, etc. Mais

  • cela ne fonctionne pas sur tous les navigateurs
  • en particulier Safari (mobile dans la plupart des cas) ignore totalement ces paramètres

J'essaierai de faire des hacks et de forcer le safari mobile à se rafraîchir, mais ce n'est pas ce que j'attends.

Je voudrais savoir si je peux:

  • gérer l'exception sans montrer à l'utilisateur tout problème existe (totalement transparent pour l'utilisateur)
  • éviter ce problème en remplaçant le nom d'utilisateur du jeton anti contrefaçon qui permettra à l'utilisateur de se connecter à nouveau sans cette exception, si mes hacks liés à la mise en cache du navigateur cessent de fonctionner dans les prochaines versions des navigateurs.
  • J'aimerais vraiment ne pas me fier au comportement du navigateur, car chacun se comporte différemment.

MISE À JOUR 1

Pour apporter quelques précisions, je sais comment gérer les erreurs dans MVC. Le problème est que cette gestion des erreurs ne résout pas du tout mon problème. L'idée de base de la gestion des erreurs est la redirection vers une page d'erreur personnalisée avec un joli message. Mais je veux empêcher cette erreur de se produire, pas la gérer de manière visible par l'utilisateur. Par poignée, j'entends attraper le remplacement du nom d'utilisateur ou toute autre action appropriée, puis continuer la connexion.

MISE À JOUR 2

J'ai ajouté la solution ci-dessous qui fonctionne pour moi.

35
Marcin

Après un certain temps d'enquête, je pense avoir trouvé un moyen de se débarrasser de cette erreur pour l'utilisateur. Ce n'est pas parfait mais au moins n'affiche pas la page d'erreur:

J'ai créé un filtre basé sur 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
    }

Ensuite, j'ai appliqué ce filtre à la connexion POST:

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

L'idée principale de cette solution est de rediriger l'exception anti-contrefaçon vers l'action d'index principal. Si l'utilisateur ne sera toujours pas authentifié, il affichera alors login page si l'utilisateur sera déjà authentifié, il affichera index page.

PDATE 1 Il y a un problème potentiel avec cette solution. Si quelqu'un se connecte avec des informations d'identification différentes, en cas d'erreur, il convient d'ajouter un runtime de connexion supplémentaire - déconnectez l'utilisateur précédent et connectez-en un nouveau. Ce scénario n'est pas géré.

19
Marcin

Si vous n'avez qu'une ou quelques fonctions affectées, la création d'un filtre peut être un peu technique. Une solution plus simple mais non générique consiste à supprimer simplement le [ValidateAntiForgeryToken] pour la méthode spécifique et ajoutez une validation manuelle après avoir vérifié si l'utilisateur est connecté.

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

Vous devriez pouvoir gérer l'exception en ajoutant un filtre d'action pour gérer votre erreur.

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

Pour ce faire, assurez-vous que les erreurs personnalisées sont activées dans votre web.config.

<customErrors mode="On"/>

Vous pouvez également jeter un œil à ceci blog pour plus d'informations sur l'erreur de gestion.

Modifier Étant donné que vous utilisez MVC4 et que le blog concerne MVC3, vous pouvez également consulter la bibliothèque MSDN - HandleErrorAttribute , mais la version ne devrait pas vraiment faire de différence.

5
Jos Vinke

Une vieille question - mais j'ai rencontré ce problème aujourd'hui, et la façon dont je l'ai résolu était en redirigeant vers l'action de déconnexion comme suit:

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

    ...
}
1
Robin Dorbell