Masalah token anti-pemalsuan (MVC 5)

122

Saya mengalami masalah dengan token anti-pemalsuan :( Saya telah membuat kelas Pengguna saya sendiri yang berfungsi dengan baik, tetapi sekarang saya mendapatkan pesan kesalahan setiap kali saya membuka halaman / Akun / Daftar . Kesalahannya adalah:

Klaim jenis ' http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier ' atau ' http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider ' adalah tidak ada di ClaimsIdentity yang disediakan. Untuk mengaktifkan dukungan token anti-pemalsuan dengan autentikasi berbasis klaim, harap verifikasi bahwa penyedia klaim yang dikonfigurasi menyediakan kedua klaim ini pada contoh ClaimsIdentity yang dihasilkannya. Jika penyedia klaim yang dikonfigurasi malah menggunakan jenis klaim yang berbeda sebagai pengenal unik, ia dapat dikonfigurasi dengan menyetel properti statis AntiForgeryConfig.UniqueClaimTypeIdentifier.

Saya menemukan artikel ini:

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

jadi saya mengubah metode Application_Start saya menjadi ini:

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

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

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
}

tetapi ketika saya melakukan itu, saya mendapatkan kesalahan ini:

Klaim jenis ' http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress ' tidak ada di ClaimsIdentity yang disediakan.

Pernahkah seseorang melewati ini sebelumnya? Jika ya, apakah Anda tahu cara mengatasinya?

Salam sebelumnya,
r3plica

Perbarui 1

Berikut adalah kelas pengguna khusus saya:

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

Profil saya hanya sederhana untuk repositori saya, terlihat seperti ini:

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

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

    string CredentialId { get; set; }
}

dan kelas Pengguna adalah kelas Microsoft.AspNet.Identity.EntityFramework.User . My AccountController terlihat seperti ini:

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

Dulu didekorasi dengan atribut [ValidateAntiForgeryToken] , tetapi di situlah atribut berhenti berfungsi.

Saya harap itu cukup kode :)

r3plica
sumber
Dapatkah Anda menunjukkan kepada kami kelas Pengguna khusus dan bagaimana Anda menggunakannya?
LostInComputer
Saya telah menambahkan kelas Pengguna khusus, ditambah cara saya menggunakannya.
r3plica
Anda menggunakan versi beta. Saya menyarankan Anda meningkatkan ke versi rilis kemudian melihat apakah masalahnya masih terjadi.
LostInComputer

Jawaban:

230

Coba setel (di global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
Alex Filipovici
sumber
33
Saya pikir penting untuk dicatat mengapa ini berfungsi: Ini memberi tahu AntiForgerykelas untuk menggunakan NameIdentifier(yang merupakan string id pengguna yang ditemukan oleh GetUserId). Terima kasih atas jawaban Mike Goodwin dalam membantu saya mempelajari ini!
Matt DeKrey
Saya mencoba "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" dan mendapat kesalahan ini "Urutan berisi lebih dari satu elemen yang cocok", dalam kasus saya ada beberapa klaim (nama, peran, dan alamat email). Bagaimana saya bisa menyelesaikan ini?
Dhanuka777
9
Saya mengatur ini di Global.asax.cs
Mike Taverne
6
Ini juga merupakan solusi jika Anda menggunakan OpenId (yaitu Azure ActiveDirectory) sebagai otentikasi Anda.
guysherman
6
Ruang nama lengkap .. Saya harus melakukan penggalian untuk mencari tahu di mana ClaimTypes diadakan. System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Mark Rowe
65

Tahukah Anda klaim apa yang Anda dapatkan di ClaimsIdentity? Jika tidak:

  1. Hapus [ValidateAntiForgeryToken]atribut tersebut
  2. Letakkan breakpoint di suatu tempat di pengontrol Anda dan hancurkan
  3. Kemudian lihat arus ClaimsIdentitydan periksa klaimnya
  4. Temukan satu yang menurut Anda akan mengidentifikasi pengguna Anda secara unik
  5. Setel AntiForgeryConfig.UniqueClaimTypeIdentifierke jenis klaim tersebut
  6. Kembalikan [ValidateAntiForgeryToken]atributnya
Mike Goodwin
sumber
3
Lebih dari sekadar memberikan jawaban makan sendok langsung, yang satu ini menceritakan latar belakang dan memungkinkan penemuan diri. :) Terima kasih banyak
NitinSingh
2
6. Kembalikan [ValidateAntiForgeryToken]atribut
Scott Fraley
1
ini sangat membantu saya. Ternyata saya mendapat klaim dari aplikasi lain yang berjalan di localhost saya, di aplikasi saya di mana tidak ada klaim yang digunakan (itulah mengapa klaim itu terdengar aneh bagi saya). Jadi ketika saya keluar dari aplikasi lain, klaim hilang dan begitu pula kesalahannya. Di lingkungan uji-langsung, situs-situs ini lebih terpisah. Jadi saya rasa saya membutuhkan solusi yang disebutkan di atas, tetapi hanya untuk pengembangan lokal.
Michel
26

Taruh saja ini di global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
mzk
sumber
Terima kasih. Yang tidak saya mengerti adalah mengapa saya harus melakukan perubahan ini, saya memperbaiki beberapa masalah berbeda yang saya alami dengan kode saya tadi malam dan semuanya bekerja dengan baik. Tanpa mengubah apa pun, saya menguji ini di komputer lain dan semuanya berfungsi hingga beberapa menit yang lalu.
Artorias2718
14

Coba buka tautan di jendela penyamaran atau hapus cookie dari domain itu (yaitu localhost).

Gurgen Sargsyan
sumber
Mengapa ini berhasil dan apa penyebab masalahnya?
mok
Ini berfungsi karena ketika Anda memiliki cookie sesi dengan pengenal nama yang tidak valid, server mencoba menggunakan pengenal yang tidak valid tanpa mengarahkan pengguna ke halaman masuk dan mendapatkan pengenal nama yang tepat.
rawel
3

Sunting: Memiliki pemahaman yang lebih besar tentang masalah ini saat ini, Anda dapat mengabaikan jawaban saya di bawah ini.

Pengaturan AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;di Application_Start () dari Global.asax.cs memperbaikinya untuk saya. Meskipun saya telah http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifiermenetapkan klaim , saya mendapatkan kesalahan yang sama seperti pada pertanyaan awal. Tapi menunjukkannya seperti di atas entah bagaimana berhasil.



Dimulai dengan MVC4, token anti-pemalsuan tidak digunakan User.Identity.Namesebagai pengenal unik. Alih-alih mencari dua klaim yang diberikan dalam pesan kesalahan.

Perbarui CATATAN: Ini tidak diperlukan Anda dapat menambahkan klaim yang hilang ke ClaimsIdentity Anda saat pengguna sedang login, seperti:

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

Perhatikan bahwa salah satu klaim mungkin sudah ada sebelumnya, dan Anda akan mendapatkan error dengan klaim duplikat jika menambahkan keduanya. Jika demikian, tambahkan saja yang hilang.

cederlof.dll
sumber
1
Saya mengerti mengapa Anda menggunakan userId sebagai "/ nameidentifier", tetapi mengapa Anda memasukkan userId sebagai "/ identityprovider"?
AaronLS
2

Di Global.asax.cs,

1. Tambahkan ruang nama ini

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

2. Tambahkan baris ini di metode Application_Start:

 protected void Application_Start()
 {
       .......
       AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
 } 
Kiran Chaudhari
sumber
Bagaimana itu menambahkan nilai lebih dari yang dijawab di atas
NitinSingh
Terima kasih telah menambahkan penggunaan. @NitinSingh Saya rasa itu menambah nilai karena saya tidak tahu mana dari tiga ruang nama potensial dalam proyek saya yang akan digunakan.
Keisha W
Setiap kali Anda menambahkan fungsionalitas baru, itu akan meminta referensi yang benar. Setelah dikompilasi, Anda harus menghapus yang tidak digunakan melalui menu Refactor dengan klik kanan
NitinSingh
0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

berfungsi untuk kasus saya, saya menggunakan ADFS Authentication.

Ashu
sumber