Anti-Fälschungs-Token-Problem (MVC 5)

121

Ich habe ein Problem mit dem Anti-Fälschungs-Token :( Ich habe meine eigene Benutzerklasse erstellt, die einwandfrei funktioniert hat. Jetzt wird jedoch eine Fehlermeldung angezeigt, wenn ich zur Seite / Account / Register gehe . Der Fehler lautet:

Ein Anspruch vom Typ " http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier " oder " http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider " lautete nicht auf der bereitgestellten ClaimsIdentity vorhanden. Stellen Sie sicher, dass der konfigurierte Anspruchsanbieter beide Ansprüche für die von ihm generierten ClaimsIdentity-Instanzen bereitstellt, um die Unterstützung von Fälschungssicherungs-Token mit anspruchsbasierter Authentifizierung zu aktivieren. Wenn der konfigurierte Anspruchsanbieter stattdessen einen anderen Anspruchstyp als eindeutige Kennung verwendet, kann dieser durch Festlegen der statischen Eigenschaft AntiForgeryConfig.UniqueClaimTypeIdentifier konfiguriert werden.

Ich habe diesen Artikel gefunden:

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

Also habe ich meine Application_Start- Methode folgendermaßen geändert :

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

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

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
}

aber wenn ich das mache, bekomme ich diesen Fehler:

Ein Anspruch vom Typ ' http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress ' war auf der angegebenen ClaimsIdentity nicht vorhanden.

Hat jemand das schon einmal gesehen? Wenn ja, wissen Sie, wie Sie es lösen können?

Prost im Voraus,
r3plica

Update 1

Hier ist meine benutzerdefinierte Benutzerklasse:

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

Mein Profil ist für meine Repositorys einfach und sieht folgendermaßen aus:

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

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

    string CredentialId { get; set; }
}

und die Benutzerklasse ist die Microsoft.AspNet.Identity.EntityFramework.User- Klasse. Mein AccountController sieht folgendermaßen aus:

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

Früher war es mit dem Attribut [ValidateAntiForgeryToken] dekoriert , aber dort funktionierte es nicht mehr.

Ich hoffe das ist genug Code :)

r3plica
quelle
Können Sie uns die benutzerdefinierte Benutzerklasse zeigen und wie Sie sie verwendet haben?
LostInComputer
Ich habe die benutzerdefinierte Benutzerklasse hinzugefügt und angegeben, wie ich sie verwende.
r3plica
Sie verwenden die Beta-Version. Ich schlage vor, dass Sie auf die Release-Version aktualisieren und dann prüfen, ob das Problem weiterhin auftritt.
LostInComputer

Antworten:

229

Versuchen Sie die Einstellung (in global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
Alex Filipovici
quelle
33
Ich denke, es ist wichtig zu beachten, warum dies funktioniert: Dies weist die AntiForgeryKlasse an, die NameIdentifier(die von gefundene Benutzer-ID-Zeichenfolge GetUserId) zu verwenden. Vielen Dank an Mike Goodwins Antwort, die mir dabei geholfen hat, dies zu lernen!
Matt DeKrey
Ich habe versucht, "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" und habe den Fehler "Sequenz enthält mehr als ein übereinstimmendes Element" erhalten, in meinem Fall gibt es mehrere Ansprüche (Name, Rolle und E-Mail-Adresse). Wie kann ich das klären?
Dhanuka777
9
Ich habe dies in Global.asax.cs
Mike Taverne
6
Dies ist auch die Lösung, wenn Sie OpenId (dh Azure ActiveDirectory) als Authentifizierung verwenden.
Jungsherman
6
Vollständige Namespaces. Ich musste ein bisschen graben, um herauszufinden, wo ClaimTypes gehalten wurde. System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Mark Rowe
65

Wissen Sie, welche Ansprüche Sie in Ihrer ClaimsIdentity erhalten? Wenn nicht:

  1. Entfernen Sie das [ValidateAntiForgeryToken]Attribut
  2. Platzieren Sie irgendwo in Ihrem Controller einen Haltepunkt und brechen Sie ihn ab
  3. Dann schauen Sie sich den Strom an ClaimsIdentityund prüfen Sie die Ansprüche
  4. Finden Sie eine, von der Sie glauben, dass sie Ihren Benutzer eindeutig identifiziert
  5. Stellen Sie den AntiForgeryConfig.UniqueClaimTypeIdentifierAnspruchstyp ein
  6. Setzen Sie das [ValidateAntiForgeryToken]Attribut zurück
Mike Goodwin
quelle
3
Diese Antwort liefert nicht nur die direkte Löffel-Feed-Antwort, sondern auch den Hintergrund und ermöglicht die Selbstfindung. :) Vielen Dank
NitinSingh
2
6. Setzen Sie das [ValidateAntiForgeryToken]Attribut zurück
Scott Fraley
1
das hat mir wirklich geholfen. Es stellte sich heraus, dass ich einen Anspruch auf eine andere Anwendung erhalten habe, die auf meinem lokalen Host ausgeführt wird, in meinem Antrag, in dem keine Ansprüche verwendet werden (weshalb die Anspruchssache für mich seltsam klang). Als ich mich von der anderen Anwendung abmeldete, waren die Ansprüche weg und der Fehler auch. In der Live-Test-Umgebung sind diese Sites stärker voneinander getrennt. Ich denke, ich brauche die oben genannte Lösung, aber nur für die lokale Entwicklung.
Michel
26

Fügen Sie dies einfach in global.asax.cs ein

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
mzk
quelle
Vielen Dank. Was ich nicht verstehe, ist, warum ich diese Änderung vornehmen musste. Ich habe ein paar verschiedene Probleme behoben, die ich letzte Nacht mit meinem Code hatte, und alles hat gut funktioniert. Ohne etwas zu ändern, habe ich dies auf einem anderen Computer getestet und alles hat bis vor ein paar Minuten funktioniert.
Artorias2718
13

Versuchen Sie, den Link im Inkognito-Fenster zu öffnen oder Cookies aus dieser Domain (dh localhost) zu löschen.

Gurgen Sargsyan
quelle
Warum funktioniert das und was ist die Ursache des Problems?
Mok
Dies funktioniert, weil der Server bei einem Sitzungscookie mit ungültiger Namenskennung versucht, die ungültige Kennung zu verwenden, ohne den Benutzer auf die Anmeldeseite umzuleiten und die richtige Namenskennung zu erhalten.
Rawel
3

Bearbeiten: Wenn Sie dieses Problem in diesem Moment besser verstehen, können Sie meine Antwort unten ignorieren.

Die Einstellung AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;in Application_Start () von Global.asax.cs hat dies für mich behoben. Obwohl ich den Anspruch http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifierfestgelegt habe, erhalte ich den gleichen Fehler wie in der ursprünglichen Frage. Aber darauf hinzuweisen, wie oben, funktioniert irgendwie.



Ab MVC4 wird das Anti-Fälschungs-Token nicht User.Identity.Nameals eindeutige Kennung verwendet. Stattdessen wird nach den beiden in der Fehlermeldung angegebenen Ansprüchen gesucht.

Update HINWEIS: Dies sollte nicht erforderlich sein. Sie können die fehlenden Ansprüche zu Ihrer ClaimsIdentity hinzufügen, wenn der Benutzer angemeldet ist.

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

Beachten Sie, dass einer der Ansprüche möglicherweise bereits von früher vorhanden ist und Sie eine Fehlermeldung mit doppelten Ansprüchen erhalten, wenn Sie beide hinzufügen. Wenn ja, fügen Sie einfach den fehlenden hinzu.

cederlof
quelle
1
Ich verstehe, warum Sie userId als "/ nameidentifier" verwenden, aber warum geben Sie die userId als "/ identityprovider" ein?
AaronLS
2

In Global.asax.cs

1. Fügen Sie diese Namespaces hinzu

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

2. Fügen Sie diese Zeile in der Methode Application_Start hinzu:

 protected void Application_Start()
 {
       .......
       AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
 } 
Kiran Chaudhari
quelle
Wie bringt es mehr Wert als die oben beantworteten
NitinSingh
Vielen Dank für das Hinzufügen der Verwendungen. @NitinSingh Ich denke, das erhöht den Wert, da ich nicht wusste, welchen der drei potenziellen Namespaces in meinem Projekt ich verwenden soll.
Keisha W
Wenn Sie eine neue Funktionalität hinzufügen, werden Sie nach korrekten Referenzen gefragt. Sobald es kompiliert ist, sollten Sie nicht verwendete über das Refactor-Menü mit der rechten
Maustaste
0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

funktioniert für meinen Fall Ich verwende ADFS-Authentifizierung.

Ashu
quelle