web-dev-qa-db-fra.com

Le jeton anti-falsification est destiné à l'utilisateur "" mais l'utilisateur actuel est "nom d'utilisateur"

Je construis une application d'une page et rencontre un problème avec les jetons anti-contrefaçon.

Je sais pourquoi le problème se produit, je ne sais tout simplement pas comment le résoudre.

Je reçois le message d'erreur lorsque les événements suivants se produisent:

  1. Un utilisateur non connecté charge une boîte de dialogue (avec un jeton anti-falsification généré)
  2. L'utilisateur ferme la boîte de dialogue
  3. L'utilisateur se connecte
  4. L'utilisateur ouvre le même dialogue 
  5. L'utilisateur soumet le formulaire dans la boîte de dialogue

Le jeton anti-falsification est destiné à l'utilisateur "" mais l'utilisateur actuel est "Nom d'utilisateur"

La raison en est que mon application est composée à 100% d'une seule page et lorsqu'un utilisateur se connecte avec succès via une publication ajax vers /Account/JsonLogin, je change simplement les vues actuelles avec les "vues authentifiées" renvoyées par le serveur, mais do ne pas recharger la page.

Je sais que c'est la raison parce que si je recharge simplement la page entre les étapes 3 et 4, il n'y a pas d'erreur.

Il semble donc que @Html.AntiForgeryToken() dans le formulaire chargé retourne toujours un jeton pour l'ancien utilisateur jusqu'à ce que la page soit rechargée.

Comment puis-je changer @Html.AntiForgeryToken() pour renvoyer un jeton pour le nouvel utilisateur authentifié?

J'injecte une nouvelle variable GenericalPrincipal avec une variable personnalisée IIdentity sur chaque Application_AuthenticateRequest afin que, dès que @Html.AntiForgeryToken() soit appelé, HttpContext.Current.User.Identity est en fait, ma propriété personnalisée Identity with IsAuthenticated définie sur true et que @Html.AntiForgeryToken semble toujours restituer un jeton pour l'ancien utilisateur, recharger la page.

114
parliament

Cela se produit car le jeton anti-falsification incorpore le nom d'utilisateur de l'utilisateur dans le jeton chiffré pour une meilleure validation. Lorsque vous appelez pour la première fois la @Html.AntiForgeryToken(), l'utilisateur n'est pas connecté. Ainsi, le jeton aura une chaîne vide pour le nom d'utilisateur. Une fois que l'utilisateur s'est connecté, il ne passera pas la validation si le jeton anti-falsification n'a pas été remplacé. pour l'utilisateur anonyme et maintenant nous avons un utilisateur authentifié avec un nom d'utilisateur connu.

Vous avez quelques options pour résoudre ce problème:

  1. Juste cette fois, laissez votre spa effectuer un POST complet. Lorsque la page sera rechargée, un jeton anti-falsification contenant le nom d'utilisateur mis à jour sera incorporé.

  2. Ayez une vue partielle avec juste @Html.AntiForgeryToken() et juste après la connexion, faites une autre demande AJAX et remplacez votre jeton anti-falsification existant par la réponse à la demande.

  3. Il suffit de désactiver le contrôle d'identité effectué par la validation anti-falsification. Ajoutez les éléments suivants à votre méthode Application_Start:AntiForgeryConfig.SuppressIdentityHeuristicChecks = true.

163
epignosisx

Pour corriger l'erreur, vous devez placer l'annotation de données OutputCache sur la page Obtenir ActionResult de connexion en tant que:

[OutputCache(NoStore=true, Duration = 0, VaryByParam= "None")] 
public ActionResult Login(string returnUrl)
20
user3401354

Cela arrive souvent avec mon application, alors j'ai décidé de chercher sur Google!

J'ai trouvé une explication simple à propos de cette erreur! L'utilisateur double-clique sur le bouton pour se connecter! Vous pouvez voir un autre utilisateur en parler sur le lien ci-dessous:

Le jeton anti-falsification fourni par MVC 4 était destiné à l'utilisateur "" mais l'utilisateur actuel est "utilisateur"

J'espère que ça aide! =)

8
Ricardo França

J'ai eu le même problème, et ce sale bidouillage l'a réparé, du moins jusqu'à ce que je puisse le réparer plus proprement.

    public ActionResult Login(string returnUrl)
    {
        if (AuthenticationManager.User.Identity.IsAuthenticated)
        {
            AuthenticationManager.SignOut();
            return RedirectToAction("Login");
        }

...

4
Saúl Jacinto

J'ai eu un problème assez spécifique mais similaire dans le processus d'inscription. Une fois que l'utilisateur a cliqué sur le lien de courrier électronique qui lui a été envoyé, il est connecté et envoyé directement à l'écran des détails du compte pour fournir des informations supplémentaires. Mon code était:

    Dim result = Await UserManager.ConfirmEmailAsync(userId, code)
    If result.Succeeded Then
        Dim appUser = Await UserManager.FindByIdAsync(userId)
        If appUser IsNot Nothing Then
            Dim signInStatus = Await SignInManager.PasswordSignInAsync(appUser.Email, password, True, shouldLockout:=False)
            If signInStatus = SignInStatus.Success Then
                Dim identity = Await UserManager.CreateIdentityAsync(appUser, DefaultAuthenticationTypes.ApplicationCookie)
                AuthenticationManager.SignIn(New AuthenticationProperties With {.IsPersistent = True}, identity)
                Return View("AccountDetails")
            End If
        End If
    End If

J'ai constaté que la vue de retour ("AccountDetails") me donnait l'exception de jeton. Je suppose que la fonction ConfirmEmail était décorée de AllowAnonymous mais que la fonction AccountDetails avait ValidateAntiForgeryToken.

Changer le retour Return Return ReturnToAction ("AccountDetails") a résolu le problème pour moi.

1
Liam
[OutputCache(NoStore=true, Duration=0, VaryByParam="None")]

public ActionResult Login(string returnUrl)

Vous pouvez tester cela en plaçant un point d'arrêt sur la première ligne de votre action Login (Get). Avant d’ajouter la directive OutputCache, le point d'arrêt était atteint lors du premier chargement, mais après avoir cliqué sur le bouton Précédent du navigateur, ce ne serait pas le cas. Après avoir ajouté la directive, le point d'arrêt doit être atteint à chaque fois. Ainsi, AntiForgeryToken sera celui qui est correct, pas celui qui est vide.

1
Marian Dalalau

La même exception se produit la plupart du temps sur le serveur de production.

Pourquoi ça arrive?

Cela se produit lorsque l'utilisateur se connecte avec des informations d'identification valides et une fois connecté et redirige vers une autre page. Après avoir appuyé sur le bouton Précédent, il affiche la page de connexion.

Comment résoudre?

Ajoutez simplement cette ligne et travaillez parfaitement, sans erreur.

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
1
Brijesh Mavani

Le message apparaît lorsque vous vous connectez alors que vous êtes déjà authentifié.

Cet assistant fait exactement la même chose que l'attribut [ValidateAntiForgeryToken].

System.Web.Helpers.AntiForgery.Validate()

Supprimez l'attribut [ValidateAntiForgeryToken] du contrôleur et placez cette aide dans la méthode action.

Ainsi, lorsque l'utilisateur est déjà authentifié, redirigez-le vers la page d'accueil ou, si ce n'est pas le cas, poursuivez la vérification du jeton anti-contrefaçon valide après cette vérification.

if (User.Identity.IsAuthenticated)
{
    return RedirectToAction("Index", "Home");
}

System.Web.Helpers.AntiForgery.Validate();

Pour essayer de reproduire l'erreur, procédez comme suit: Si vous êtes sur votre page de connexion et que vous n'êtes pas authentifié. Si vous dupliquez l'onglet et que vous vous connectez avec le deuxième onglet . Et si vous revenez au premier onglet de la page de connexion et que vous essayez de vous connecter sans recharger la page ... vous avez cette erreur.

0
A. Morel

J'ai eu le même problème avec une seule application ASP.NET MVC Core. Je l'ai résolu en définissant HttpContext.User dans toutes les actions du contrôleur qui modifient les revendications d'identité actuelles (MVC ne le faisant que pour les demandes suivantes, comme indiqué dans ici ). J'ai utilisé un filtre de résultat à la place du middleware pour ajouter les cookies antiforgery à mes réponses, ce qui garantissait qu'elles étaient uniquement générées après le retour de l'action MVC.

Contrôleur (NB. Je gère les utilisateurs avec ASP.NET Core Identity):

[Authorize]
[ValidateAntiForgeryToken]
public class AccountController : Controller
{
    private SignInManager<IdentityUser> signInManager;
    private UserManager<IdentityUser> userManager;
    private IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory;

    public AccountController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory)
    {
        this.signInManager = signInManager;
        this.userManager = userManager;
        this.userClaimsPrincipalFactory = userClaimsPrincipalFactory;
    }

    [HttpPost]
    [AllowAnonymous]
    public async Task<IActionResult> Login(string username, string password)
    {
        if (username == null || password == null)
        {
            return BadRequest(); // Alias of 400 response
        }

        var result = await signInManager.PasswordSignInAsync(username, password, false, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            var user = await userManager.FindByNameAsync(username);

            // Must manually set the HttpContext user claims to those of the logged
            // in user. Otherwise MVC will still include a XSRF token for the "null"
            // user and token validation will fail. (MVC appends the correct token for
            // all subsequent reponses but this isn't good enough for a single page
            // app.)
            var principal = await userClaimsPrincipalFactory.CreateAsync(user);
            HttpContext.User = principal;

            return Json(new { username = user.UserName });
        }
        else
        {
            return Unauthorized();
        }
    }

    [HttpPost]
    public async Task<IActionResult> Logout()
    {
        await signInManager.SignOutAsync();

        // Removing identity claims manually from the HttpContext (same reason
        // as why we add them manually in the "login" action).
        HttpContext.User = null;

        return Json(new { result = "success" });
    }
}

Filtre de résultat pour ajouter des cookies antiforgery:

public class XSRFCookieFilter : IResultFilter
{
    IAntiforgery antiforgery;

    public XSRFCookieFilter(IAntiforgery antiforgery)
    {
        this.antiforgery = antiforgery;
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var HttpContext = context.HttpContext;
        AntiforgeryTokenSet tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
        HttpContext.Response.Cookies.Append(
            "MyXSRFFieldTokenCookieName",
            tokenSet.RequestToken,
            new CookieOptions() {
                // Cookie needs to be accessible to Javascript so we
                // can append it to request headers in the browser
                HttpOnly = false
            } 
        );
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {

    }
}

Extrait de Startup.cs:

public partial class Startup
{
    public Startup(IHostingEnvironment env)
    {
        //...
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {

        //...

        services.AddAntiforgery(options =>
        {
            options.HeaderName = "MyXSRFFieldTokenHeaderName";
        });


        services.AddMvc(options =>
        {
            options.Filters.Add(typeof(XSRFCookieFilter));
        });

        services.AddScoped<XSRFCookieFilter>();

        //...
    }

    public void Configure(
        IApplicationBuilder app,
        IHostingEnvironment env,
        ILoggerFactory loggerFactory)
    {
        //...
    }
}
0
Ned Howley