Problème de jeton anti-contrefaçon (MVC 5)

122

Je rencontre un problème avec le jeton anti-contrefaçon: (J'ai créé ma propre classe User qui a bien fonctionné mais maintenant j'obtiens une erreur chaque fois que je vais à la page / Compte / Inscription . L'erreur est:

Une revendication de type « http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier » ou « http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider » était non présent sur le ClaimsIdentity fourni. Pour activer la prise en charge des jetons anti-falsification avec l'authentification basée sur les revendications, veuillez vérifier que le fournisseur de revendications configuré fournit ces deux revendications sur les instances de ClaimsIdentity qu'il génère. Si le fournisseur de revendications configuré utilise à la place un type de revendication différent comme identificateur unique, il peut être configuré en définissant la propriété statique AntiForgeryConfig.UniqueClaimTypeIdentifier.

J'ai trouvé cet article:

http://stack247.wordpress.com/2013/02/22/antiforgerytoken-a-claim-of-type-nameidentifier-or-identityprovider-was-not-present-on-provided-claimsidentity/

j'ai donc changé ma méthode Application_Start en ceci:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
}

mais quand je fais cela, j'obtiens cette erreur:

Une revendication de type « http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress » n'était pas présente sur le ClaimsIdentity fourni.

Est-ce que quelqu'un à déjà rencontré cela avant? Si oui, savez-vous comment le résoudre?

Merci d'avance,
bientôt r3plica

Mise à jour 1

Voici ma classe d'utilisateurs personnalisée:

public class Profile : User, IProfile
{
    public Profile()
        : base()
    {
        this.LastLoginDate = DateTime.UtcNow;
        this.DateCreated = DateTime.UtcNow;
    }

    public Profile(string userName)
        : base(userName)
    {
        this.CreatedBy = this.Id;

        this.LastLoginDate = DateTime.UtcNow;
        this.DateCreated = DateTime.UtcNow;

        this.IsApproved = true;
    }

    [NotMapped]
    public HttpPostedFileBase File { get; set; }

    [Required]
    public string CompanyId { get; set; }

    [Required]
    public string CreatedBy { get; set; }
    public string ModifiedBy { get; set; }

    public DateTime DateCreated { get; set; }
    public DateTime? DateModified { get; set; }
    public DateTime LastLoginDate { get; set; }

    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredTitle")]
    public string Title { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredFirstName")]
    public string Forename { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredLastName")]
    public string Surname { get; set; }

    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredEmail")]
    public string Email { get; set; }
    public string JobTitle { get; set; }
    public string Telephone { get; set; }
    public string Mobile { get; set; }
    public string Photo { get; set; }
    public string LinkedIn { get; set; }
    public string Twitter { get; set; }
    public string Facebook { get; set; }
    public string Google { get; set; }
    public string Bio { get; set; }

    public string CompanyName { get; set; }

    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredCredentialId")]
    public string CredentialId { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredSecurityCode")]
    public bool IsLockedOut { get; set; }
    public bool IsApproved { get; set; }

    [Display(Name = "Can only edit own assets")]
    public bool CanEditOwn { get; set; }
    [Display(Name = "Can edit assets")]
    public bool CanEdit { get; set; }
    [Display(Name = "Can download assets")]
    public bool CanDownload { get; set; }
    [Display(Name = "Require approval to upload assets")]
    public bool RequiresApproval { get; set; }
    [Display(Name = "Can approve assets")]
    public bool CanApprove { get; set; }
    [Display(Name = "Can synchronise assets")]
    public bool CanSync { get; set; }

    public bool AgreedTerms { get; set; }
    public bool Deleted { get; set; }
}

public class ProfileContext : IdentityStoreContext
{
    public ProfileContext(DbContext db)
        : base(db)
    {
        this.Users = new UserStore<Profile>(this.DbContext);
    }
}

public class ProfileDbContext : IdentityDbContext<Profile, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
}

Mon profil est simplement simple pour mes référentiels, ressemble à ceci:

public interface IProfile
{
    string Id { get; set; }
    string CompanyId { get; set; }

    string UserName { get; set; }
    string Email { get; set; }

    string CredentialId { get; set; }
}

et la classe User est la classe Microsoft.AspNet.Identity.EntityFramework.User . Mon AccountController ressemble à ceci:

[Authorize]
public class AccountController : Controller
{
    public IdentityStoreManager IdentityStore { get; private set; }
    public IdentityAuthenticationManager AuthenticationManager { get; private set; }

    public AccountController() 
    {
        this.IdentityStore = new IdentityStoreManager(new ProfileContext(new ProfileDbContext()));
        this.AuthenticationManager = new IdentityAuthenticationManager(this.IdentityStore);
    }

    //
    // GET: /Account/Register
    [AllowAnonymous]
    public ActionResult Register()
    {
        return View();
    }

    //
    // POST: /Account/Register
    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            try
            {
                // Create a profile, password, and link the local login before signing in the user
                var companyId = Guid.NewGuid().ToString();
                var user = new Profile(model.UserName)
                {
                    CompanyId = companyId,
                    Title = model.Title,
                    Forename = model.Forename,
                    Surname = model.Surname,
                    Email = model.Email,
                    CompanyName = model.CompanyName,
                    CredentialId = model.CredentialId
                };

                if (await IdentityStore.CreateLocalUser(user, model.Password))
                {
                    //Create our company
                    var company = new Skipstone.Web.Models.Company()
                    {
                        Id = companyId,
                        CreatedBy = user.Id,
                        ModifiedBy = user.Id,
                        Name = model.CompanyName
                    };

                    using (var service = new CompanyService())
                    {
                        service.Save(company);
                    }

                    await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
                    return RedirectToAction("Setup", new { id = companyId });
                }
                else
                {
                    ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
                }
            }
            catch (IdentityException e)
            {
                ModelState.AddModelError("", e.Message);
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    //
    // POST: /Account/Setup
    public ActionResult Setup(string id)
    {
        var userId = User.Identity.GetUserId();
        using (var service = new CompanyService())
        {
            var company = service.Get(id);
            var profile = new Profile()
            {
                Id = userId,
                CompanyId = id
            };

            service.Setup(profile);

            return View(company);
        }
    }
}

Il était décoré avec l' attribut [ValidateAntiForgeryToken] , mais c'est là qu'il a cessé de fonctionner.

J'espère que c'est assez de code :)

r3plica
la source
Pouvez-vous nous montrer la classe User personnalisée et comment vous l'avez utilisée?
LostInComputer
J'ai ajouté la classe User personnalisée, ainsi que la façon dont je l'utilise.
r3plica
Vous utilisez la version bêta. Je vous suggère de mettre à niveau vers la version finale, puis de voir si le problème persiste.
LostInComputer

Réponses:

230

Essayez de définir (dans global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
Alex Filipovici
la source
33
Je pense qu'il est important de noter pourquoi cela fonctionne: cela indique à la AntiForgeryclasse d'utiliser le NameIdentifier(qui est la chaîne d'identifiant utilisateur trouvée par GetUserId). Merci à la réponse de Mike Goodwin qui m'a aidé à apprendre cela!
Matt DeKrey
J'ai essayé "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" et j'ai obtenu cette erreur "La séquence contient plus d'un élément correspondant", dans mon cas, il y a plusieurs revendications (nom, rôle et adresse e-mail). Comment puis-je régler ce problème?
Dhanuka777
9
J'ai mis cela dans Global.asax.cs
Mike Taverne
6
C'est également la solution si vous utilisez OpenId (c'est-à-dire Azure ActiveDirectory) comme authentification.
Guysherman
6
Espaces de noms complets .. J'ai dû faire quelques recherches pour savoir où ClaimTypes était conservé. System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Mark Rowe
65

Savez-vous quelles sont les réclamations que vous recevez dans votre ClaimsIdentity? Si non:

  1. Retirer le [ValidateAntiForgeryToken] attribut
  2. Mettez un point d'arrêt quelque part dans votre contrôleur et arrêtez-le
  3. Puis regardez le courant ClaimsIdentity et examinez les revendications
  4. Trouvez-en un qui, selon vous, identifiera de manière unique votre utilisateur
  5. Définissez le AntiForgeryConfig.UniqueClaimTypeIdentifiersur ce type de revendication
  6. Remettre l' [ValidateAntiForgeryToken]attribut
Mike Goodwin
la source
3
Plus que de fournir la réponse directe à la cuillère, celle-ci raconte le contexte et permet la découverte de soi. :) Merci beaucoup
NitinSingh
2
6. Remettez l' [ValidateAntiForgeryToken]attribut
Scott Fraley
1
cela m'a vraiment aidé. Il s'est avéré que j'avais une réclamation pour une autre application exécutée sur mon hôte local, dans mon application où aucune réclamation n'est utilisée (c'est pourquoi la chose des réclamations m'a semblé étrange). Ainsi, lorsque je me suis déconnecté de l'autre application, les revendications avaient disparu, tout comme l'erreur. Dans l'environnement de test en direct, ces sites sont plus séparés. Je pense donc que j'ai besoin de la solution mentionnée ci-dessus, mais uniquement pour le développement local.
Michel le
26

Mettez simplement ceci dans global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
mzk
la source
Merci. Ce que je ne comprends pas, c'est pourquoi j'ai dû faire ce changement, j'ai corrigé quelques problèmes différents que j'avais avec mon code la nuit dernière et tout fonctionnait très bien. Sans rien changer, j'ai testé cela sur une autre machine et tout a fonctionné jusqu'à il y a quelques minutes.
Artorias2718
14

Essayez d'ouvrir le lien dans la fenêtre de navigation privée ou effacez le cookie de ce domaine (c'est-à-dire localhost).

Gurgen Sargsyan
la source
Pourquoi cela fonctionne-t-il et quelle est la cause du problème?
mok
Cela fonctionne car lorsque vous avez un cookie de session avec un identifiant de nom non valide, le serveur essaie d'utiliser l'identifiant non valide sans rediriger l'utilisateur vers la page de connexion et obtenir l'identifiant de nom approprié.
rawel
3

Edit: Ayant une meilleure compréhension de ce problème en ce moment, vous pouvez ignorer ma réponse ci-dessous.

La configuration AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;dans Application_Start () de Global.asax.cs l'a corrigé pour moi. Même si j'ai l' http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifierensemble de revendications , j'obtiens la même erreur que dans la question d'origine. Mais le signaler comme ci-dessus fonctionne en quelque sorte.



À partir de MVC4, le jeton anti-contrefaçon n'utilise pas User.Identity.Name comme identifiant unique. Au lieu de cela, il recherche les deux revendications données dans le message d'erreur.

REMARQUE de mise à jour: Cela ne devrait pas être nécessaire.Vous pouvez ajouter les revendications manquantes à votre ClaimsIdentity lorsque l'utilisateur est connecté, comme ceci:

string userId = TODO;
var identity = System.Web.HttpContext.Current.User.Identity as ClaimsIdentity;
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", userId));
identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userId));

Notez que l'une des revendications peut déjà être là avant et que vous obtiendrez une erreur avec des revendications en double si vous ajoutez les deux. Si tel est le cas, ajoutez simplement celui qui manque.

cederlof
la source
1
Je comprends pourquoi vous utilisez userId comme "/ nameidentifier", mais pourquoi mettez-vous le userId comme "/ identityprovider"?
AaronLS
2

Dans Global.asax.cs,

1.Ajoutez ces espaces de noms

using System.Web.Helpers;
using System.Security.Claims;

Ajoutez cette ligne dans la méthode Application_Start:

 protected void Application_Start()
 {
       .......
       AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
 } 
Kiran Chaudhari
la source
Comment cela ajoute-t-il plus de valeur que ceux qui ont répondu ci
NitinSingh
Merci d'avoir ajouté les utilisations. @NitinSingh Je pense que cela ajoute plus de valeur parce que je ne savais pas lequel des trois espaces de noms potentiels de mon projet utiliser.
Keisha W
Chaque fois que vous ajoutez une nouvelle fonctionnalité, il vous demandera des références correctes. Une fois compilé, vous devez supprimer les inutilisés via le menu Refactor sur un clic droit
NitinSingh
0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

fonctionne pour mon cas, j'utilise l'authentification ADFS.

Ashu
la source