Проблема з маркуванням підробки (MVC 5)


122

У мене виникають проблеми з маркером проти підробки: (я створив власний клас користувача, який працював нормально, але тепер я отримую помилку щоразу, коли переходжу на сторінку / Обліковий запис / Реєстрація . Помилка:

Заява типу " http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier " або " http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider " була немає на наданій заявці ідентичності. Щоб увімкнути підтримку маркерів проти підробки за допомогою аутентифікації на основі претензій, переконайтеся, що налаштований постачальник претензій надає обидві ці заявки у створених ним випадках ClaimsIdentity. Якщо постачальник налаштованих претензій замість цього використовує інший тип претензії як унікальний ідентифікатор, це можна налаштувати, встановивши статичну властивість AntiForgeryConfig.UniqueClaimTypeIdentifier.

Я знайшов цю статтю:

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

тому я змінив свій метод Application_Start на це:

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

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

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
}

але коли я це роблю, я отримую цю помилку:

Заява типу " http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress " відсутня на наданій ClaimsIdentity.

Хтось раніше стикався з цим? Якщо так, чи знаєте ви, як це вирішити?


Будемо заздалегідь, r3plica

Оновлення 1

Ось мій користувацький клас користувача:

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

Я профілю для своїх сховищ просто простий, виглядає так:

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

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

    string CredentialId { get; set; }
}

а клас User - клас Microsoft.AspNet.Identity.EntityFramework.User . Мій рахунокController виглядає так:

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

Раніше він був прикрашений атрибутом [ValidateAntiForgeryToken] , але саме там він перестав працювати.

Я сподіваюся, що цього коду достатньо :)


Чи можете ви показати нам користувацький клас користувача та як ним користуватися?
LostInComputer

Я додав користувацький клас користувача, а також те, як я ним користуюся.
r3plica

Ви використовуєте бета-версію. Я пропоную вам оновити до версії, а потім побачити, чи проблема все-таки виникає.
LostInComputer

Відповіді:


230

Спробуйте встановити (у global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

33
Я думаю, що важливо відзначити, чому це працює: Це вказує AntiForgeryкласу використовувати NameIdentifier(що є рядом ідентифікатора користувача GetUserId). Завдяки відповіді Майка Гудвіна, яка допомагає мені дізнатися це!
Метт Декрей

Я спробував "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" і отримала цю помилку "Послідовність містить більше одного відповідного елемента", в моєму випадку є кілька претензій (ім'я, роль та адресу електронної пошти). Як я можу розібратися в цьому?
Dhanuka777

9
Я встановив це в Global.asax.cs
Майк Таверн

6
Це рішення також, якщо ви використовуєте OpenId (тобто Azure ActiveDirectory) як автентифікатор.
guysherman

6
Повні простори імен .. Мені довелося зробити кілька копань, щоб дізнатися, де утримуються ClaimTypes. System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Марк Роу

65

Чи знаєте ви, які претензії ви маєте у вашій претензії? Якщо ні:

  1. Видаліть [ValidateAntiForgeryToken]атрибут
  2. Поставте точку розриву десь у контролері і перервіться на ній
  3. Потім подивіться на течію ClaimsIdentityта вивчіть претензії
  4. Знайдіть той, який, на вашу думку, однозначно визначить вашого користувача
  5. Встановіть цей AntiForgeryConfig.UniqueClaimTypeIdentifierтип претензії
  6. Поверніть [ValidateAntiForgeryToken]атрибут

3
Більше ніж відповідь на пряму подачу ложки, цей розповідає про тло та дозволяє самостійно розкривати :) Велике спасибі
NitinSingh

2
6. Поставте назад [ValidateAntiForgeryToken]атрибут
Скотт Фралі

1
це мені справді допомогло. Виявилося, що я отримав претензію до іншої програми, що працює на моєму локальному хості, у моїй заявці, де немає жодних претензій (через що річ з претензіями мені звучала дивно). Тож коли я вийшов з іншої програми, претензій не було, і так сталася помилка. У прямому тестовому середовищі ці сайти є більш відокремленими. Тож я думаю, що мені потрібне вищезгадане рішення, але лише для місцевого розвитку.
Мішель

26

Просто покладіть це на global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

Дякую. Чого я не розумію, чому мені довелося внести цю зміну, я вирішив кілька різних проблем, які виникли у мене зі своїм кодом минулої ночі, і все працювало чудово. Не змінюючи нічого, я протестував це на іншій машині, і все це працювало до декількох хвилин тому.
Artorias2718

14

Спробуйте відкрити посилання у вікні анонімного перегляду або очистити файл cookie з цього домену (тобто localhost).


Чому це працює і в чому причина проблеми?
мок

Це працює, тому що, коли у вас є сесійне cookie з недійсним ідентифікатором імені, сервер намагається використовувати недійсний ідентифікатор, не перенаправляючи користувача на сторінку входу та отримуючи належний ідентифікатор імені.
rawel

3

Редагувати: Якщо ви більше розумієте цю проблему в цей момент, ви можете знехтувати моєю відповіддю нижче.

Налаштування AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;в Application_Start () Global.asax.cs виправило це для мене. Незважаючи на те, що у мене встановлено претензію http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier, я отримую ту ж помилку, що і в оригінальному запитанні. Але вказівка ​​на неї, як вище, якось працює.



Починаючи з MVC4, маркер анти-підробки не використовується User.Identity.Nameяк унікальний ідентифікатор. Натомість він шукає дві претензії, подані в повідомленні про помилку.

Оновлення ПРИМІТКА. Це не повинно бути потрібним. Ви можете додати відсутню претензію до вашої ClaimsIdentity під час входу користувача, наприклад:

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

Зауважте, що одна з заявок може бути вже раніше, і ви отримаєте помилку з повторюваними заявками, якщо додати обидва. Якщо так, просто додайте ту, яка відсутня.


1
Я розумію, чому ви використовуєте userId як "/ nameidentifier", але чому ви ставите UserId як "/ identprovider"?
AaronLS

2

У Global.asax.cs,

1.Додайте ці простори імен

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

2.Додайте цей рядок у методі Application_Start:

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

Як це додає більше значення, ніж відповіді вище
NitinSingh

Дякуємо, що додали вставки. @NitinSingh Я думаю, що це додає більшої цінності, оскільки я не знав, який із трьох потенційних просторів імен у своєму проекті використовувати.
Кейша Ш

Щоразу, коли ви додаєте новий функціонал, він вимагатиме правильних посилань. Після компіляції ви повинні видалити невикористані з меню Рефактор правою кнопкою миші
NitinSingh

0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

працює для мого випадку, я використовую аутентифікацію ADFS.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.