Emissione di token anti-contraffazione (MVC 5)


122

Sto riscontrando un problema con il token anti-contraffazione :( Ho creato la mia classe utente che ha funzionato bene ma ora ricevo un errore ogni volta che vado alla pagina / Account / Register . L'errore è:

Una rivendicazione di tipo " http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier " o " http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider " era non presente sulla ClaimsIdentity fornita. Per abilitare il supporto del token anti-contraffazione con l'autenticazione basata sulle attestazioni, verificare che il provider di attestazioni configurato fornisca entrambe queste attestazioni nelle istanze di ClaimsIdentity che genera. Se il provider di attestazioni configurato utilizza invece un tipo di attestazione diverso come identificatore univoco, può essere configurato impostando la proprietà statica AntiForgeryConfig.UniqueClaimTypeIdentifier.

Ho trovato questo articolo:

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

quindi ho cambiato il mio metodo Application_Start in questo:

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

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

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
}

ma quando lo faccio, ottengo questo errore:

Una rivendicazione di tipo " http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress " non era presente nella ClaimsIdentity fornita.

Qualcuno l'ha mai visto prima? Se è così, sai come risolverlo?

Saluti in anticipo,
r3plica

Aggiorna 1

Ecco la mia classe utente personalizzata:

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>
{
}

Il mio profilo è semplice per i miei repository, assomiglia a questo:

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

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

    string CredentialId { get; set; }
}

e la classe User è la classe Microsoft.AspNet.Identity.EntityFramework.User . Il mio AccountController ha questo aspetto:

[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);
        }
    }
}

In passato era decorato con l' attributo [ValidateAntiForgeryToken] , ma è lì che ha smesso di funzionare.

Spero che sia abbastanza codice :)


Puoi mostrarci la classe utente personalizzata e come l'hai usata?
LostInComputer

Ho aggiunto la classe utente personalizzata, oltre a come la sto usando.
r3plica

Stai utilizzando la versione beta. Ti suggerisco di aggiornare alla versione di rilascio, quindi vedere se il problema persiste.
LostInComputer

Risposte:


230

Prova a impostare (in global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

33
Penso che sia importante notare perché funziona: questo dice alla AntiForgeryclasse di usare NameIdentifier(che è la stringa dell'ID utente trovata da GetUserId). Grazie alla risposta di Mike Goodwin per avermi aiutato a imparare questo!
Matt DeKrey

Ho provato "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" e ho ricevuto questo errore "La sequenza contiene più di un elemento corrispondente", nel mio caso ci sono diversi reclami (nome, ruolo e indirizzo email). Come posso risolverlo?
Dhanuka777

9
Ho impostato questo in Global.asax.cs
Mike Taverne

6
Questa è anche la soluzione se utilizzi OpenId (ovvero Azure ActiveDirectory) come autenticazione.
guysherman

6
Spazi dei nomi completi .. Ho dovuto scavare per scoprire dove si tenevano i ClaimTypes. System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Mark Rowe

65

Sai quali affermazioni ottieni nella tua ClaimsIdentity? Altrimenti:

  1. Rimuovi il [ValidateAntiForgeryToken] attributo
  2. Metti un punto di interruzione da qualche parte nel tuo controller e interrompilo
  3. Quindi guarda la corrente ClaimsIdentity ed esamina le affermazioni
  4. Trovane uno che pensi possa identificare in modo univoco il tuo utente
  5. Imposta il AntiForgeryConfig.UniqueClaimTypeIdentifiertipo di reclamo
  6. Rimetti l' [ValidateAntiForgeryToken]attributo

3
Più che fornire la risposta diretta con il cucchiaio, questa racconta i retroscena e consente la scoperta di sé. :) Grazie mille
NitinSingh

2
6. Riposizionare l' [ValidateAntiForgeryToken]attributo
Scott Fraley

1
questo mi ha davvero aiutato. Si è scoperto che ho ricevuto un reclamo per un'altra applicazione in esecuzione sul mio localhost, nella mia applicazione in cui non sono stati utilizzati reclami (motivo per cui la cosa delle affermazioni mi è sembrata strana). Quindi, quando mi sono disconnesso dall'altra applicazione, le richieste erano sparite e così anche l'errore. Nell'ambiente di test dal vivo questi siti sono più separati. Quindi penso di aver bisogno della soluzione di cui sopra, ma solo per lo sviluppo locale.
Michel

26

Mettilo in global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

Grazie. Quello che non capisco è il motivo per cui ho dovuto apportare questa modifica, ho risolto alcuni problemi diversi che avevo con il mio codice la scorsa notte e tutto ha funzionato perfettamente. Senza cambiare nulla, l'ho testato su un'altra macchina e tutto ha funzionato fino a pochi minuti fa.
Artorias2718

14

Prova ad aprire il link nella finestra di navigazione in incognito o a cancellare i cookie da quel dominio (ad es. Localhost).


Perché funziona e qual è la causa del problema?
mok

Questo funziona perché quando si dispone di un cookie di sessione con identificatore nome non valido, il server tenta di utilizzare l'identificatore non valido senza reindirizzare l'utente alla pagina di accesso e ottenere l'identificatore nome corretto.
rawel

3

Modifica: avendo una maggiore comprensione di questo problema in questo momento, puoi ignorare la mia risposta di seguito.

L'impostazione AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;in Application_Start () di Global.asax.cs lo ha risolto per me. Anche se ho http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifierimpostato il reclamo , ricevo lo stesso errore della domanda originale. Ma segnalarlo come sopra in qualche modo funziona.



A partire da MVC4 il token anti-contraffazione non viene utilizzato User.Identity.Namecome identificatore univoco. Invece cerca le due affermazioni fornite nel messaggio di errore.

Aggiorna NOTA: non dovrebbe essere necessario Puoi aggiungere le attestazioni mancanti a ClaimsIdentity quando l'utente ha effettuato l'accesso, in questo modo:

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));

Tieni presente che una delle rivendicazioni potrebbe essere già presente in precedenza e, se le aggiungi entrambe, riceverai un errore con rivendicazioni duplicate. In tal caso, aggiungi semplicemente quello mancante.


1
Capisco perché stai usando userId come "/ nameidentifier", ma perché stai inserendo userId come "/ identityprovider"?
AaronLS

2

In Global.asax.cs,

1.Aggiungi questi spazi dei nomi

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

2.Aggiungi questa riga nel metodo Application_Start:

 protected void Application_Start()
 {
       .......
       AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
 } 

In che modo aggiunge più valore di quelli sopra indicati
NitinSingh

Grazie per aver aggiunto gli utilizzi. @NitinSingh Penso che aggiunga più valore perché non sapevo quale dei tre potenziali spazi dei nomi nel mio progetto usare.
Keisha W

Ogni volta che aggiungi una nuova funzionalità, richiederà i riferimenti corretti. Una volta compilato, dovresti rimuovere quelli inutilizzati tramite il menu Refactor al clic destro
NitinSingh

0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

funziona per il mio caso sto usando l'autenticazione ADFS.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.