ปัญหาโทเค็นต่อต้านการปลอมแปลง (MVC 5)


122

ฉันมีปัญหากับโทเค็นต่อต้านการปลอมแปลง :( ฉันได้สร้างคลาส User ของตัวเองซึ่งใช้งานได้ดี แต่ตอนนี้ฉันได้รับข้อผิดพลาดทุกครั้งที่ฉันไปที่หน้า/ บัญชี / ลงทะเบียนข้อผิดพลาดคือ:

การอ้างสิทธิ์ประเภท " http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier " หรือ " http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider " คือ ไม่มีอยู่ใน ClaimsIdentity ที่ระบุ หากต้องการเปิดใช้งานการสนับสนุนโทเค็นต่อต้านการปลอมแปลงด้วยการตรวจสอบสิทธิ์ตามการอ้างสิทธิ์โปรดตรวจสอบว่าผู้ให้บริการการอ้างสิทธิ์ที่กำหนดค่าไว้ให้การอ้างสิทธิ์ทั้งสองนี้ในอินสแตนซ์ 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 AccountControllerของฉันมีลักษณะดังนี้:

[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]แต่นั่นคือจุดที่หยุดทำงาน

ฉันหวังว่าจะเป็นรหัสที่เพียงพอ :)


คุณสามารถแสดงคลาส User ที่กำหนดเองและวิธีการใช้งานให้เราดูได้ไหม
LostInComputer

ฉันได้เพิ่มคลาสผู้ใช้ที่กำหนดเองรวมถึงวิธีการใช้งาน
r3plica

คุณกำลังใช้รุ่นเบต้า ฉันขอแนะนำให้คุณอัปเกรดเป็นเวอร์ชันที่วางจำหน่ายแล้วดูว่าปัญหายังคงเกิดขึ้นหรือไม่
LostInComputer

คำตอบ:


230

ลองตั้งค่า (ใน global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

33
ฉันคิดว่าสิ่งสำคัญคือต้องสังเกตว่าเหตุใดจึงได้ผล: สิ่งนี้จะบอกให้AntiForgeryคลาสใช้NameIdentifier(ซึ่งเป็นสตริงรหัสผู้ใช้ที่พบโดยGetUserId) ขอบคุณคำตอบของ Mike Goodwin ที่ช่วยให้ฉันเรียนรู้สิ่งนี้!
Matt DeKrey

ฉันลอง "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" และได้รับข้อผิดพลาดนี้ "ลำดับมีองค์ประกอบที่ตรงกันมากกว่าหนึ่งรายการ" ในกรณีของฉันมีการอ้างสิทธิ์หลายรายการ (ชื่อบทบาทและที่อยู่อีเมล) ฉันจะแยกแยะสิ่งนี้ได้อย่างไร
Dhanuka777

9
ฉันตั้งค่านี้ใน Global.asax.cs
Mike Taverne

6
นี่เป็นวิธีแก้ปัญหาหากคุณใช้ OpenId (เช่น Azure ActiveDirectory) เป็น authenticaiton ของคุณ
guysherman

6
เนมสเปซแบบเต็ม .. ฉันต้องขุดคุ้ยเพื่อดูว่า ClaimTypes อยู่ที่ไหน System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Mark Rowe

65

คุณรู้หรือไม่ว่าคุณได้รับการอ้างสิทธิ์อะไรใน ClaimsIdentity ของคุณ? ถ้าไม่:

  1. ลบ[ValidateAntiForgeryToken]แอตทริบิวต์
  2. วางเบรกพอยต์ไว้ที่ไหนสักแห่งในคอนโทรลเลอร์ของคุณและทำลายมัน
  3. จากนั้นดูที่ปัจจุบันClaimsIdentityและตรวจสอบการอ้างสิทธิ์
  4. ค้นหาสิ่งที่คุณคิดว่าจะระบุผู้ใช้ของคุณโดยไม่ซ้ำใคร
  5. ตั้งค่าเป็นAntiForgeryConfig.UniqueClaimTypeIdentifierประเภทการอ้างสิทธิ์นั้น
  6. ใส่[ValidateAntiForgeryToken]แอตทริบิวต์กลับ

3
มากกว่าการให้คำตอบป้อนช้อนโดยตรงคำตอบนี้จะบอกพื้นหลังและช่วยให้ค้นพบตัวเองได้ :) ขอบคุณมาก
NitinSingh

2
6. ใส่[ValidateAntiForgeryToken]แอตทริบิวต์กลับ
Scott Fraley

1
สิ่งนี้ช่วยฉันได้จริงๆ ปรากฎว่าฉันได้รับการอ้างสิทธิ์จากแอปพลิเคชันอื่นที่ทำงานบน localhost ของฉันในแอปพลิเคชันของฉันที่ไม่มีการอ้างสิทธิ์ใด ๆ (ซึ่งเป็นสาเหตุที่การอ้างสิทธิ์ฟังดูแปลกสำหรับฉัน) ดังนั้นเมื่อฉันออกจากแอปพลิเคชันอื่นการอ้างสิทธิ์ก็หายไปและข้อผิดพลาดก็เช่นกัน ในสภาพแวดล้อมการทดสอบจริงไซต์เหล่านี้จะแยกออกจากกันมากขึ้น ดังนั้นฉันคิดว่าฉันต้องการวิธีแก้ปัญหาดังกล่าวข้างต้น แต่เพื่อการพัฒนาท้องถิ่นเท่านั้น
Michel

26

เพียงใส่สิ่งนี้ใน global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

ขอบคุณ สิ่งที่ฉันไม่ได้รับคือสาเหตุที่ฉันต้องทำการเปลี่ยนแปลงนี้ฉันแก้ไขปัญหาต่าง ๆ บางอย่างที่ฉันพบกับรหัสของฉันเมื่อคืนนี้และทุกอย่างก็ใช้ได้ดี ฉันทดสอบสิ่งนี้กับเครื่องอื่นโดยไม่เปลี่ยนแปลงอะไรเลยและทุกอย่างก็ใช้งานได้จนถึงไม่กี่นาทีที่ผ่านมา
Artorias2718

14

ลองเปิดลิงก์ในหน้าต่างที่ไม่ระบุตัวตนหรือล้างคุกกี้จากโดเมนนั้น (เช่น localhost)


ทำไมถึงใช้งานได้และอะไรคือสาเหตุของปัญหา?
โมก

สิ่งนี้ใช้ได้ผลเนื่องจากเมื่อคุณมีคุกกี้เซสชันที่มีตัวระบุชื่อที่ไม่ถูกต้องเซิร์ฟเวอร์จะพยายามใช้ตัวระบุที่ไม่ถูกต้องโดยไม่เปลี่ยนเส้นทางผู้ใช้ไปยังหน้าล็อกอินและรับตัวระบุชื่อที่เหมาะสม
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 เป็น "/ identityprovider"
AaronLS

2

ใน Global.asax.cs

1. เพิ่มเนมสเปซเหล่านี้

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

2. เพิ่มบรรทัดนี้ใน method Application_Start:

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

มันจะเพิ่มมูลค่าได้มากกว่าที่ตอบข้างบนอย่างไร
NitinSingh

ขอบคุณที่เพิ่มการใช้งาน @NitinSingh ฉันคิดว่ามันเพิ่มมูลค่ามากขึ้นเพราะฉันไม่รู้ว่าจะใช้เนมสเปซใดในโครงการของฉัน
Keisha W

เมื่อใดก็ตามที่คุณเพิ่มฟังก์ชันการทำงานใหม่จะขอข้อมูลอ้างอิงที่ถูกต้อง เมื่อรวบรวมแล้วคุณควรลบสิ่งที่ไม่ได้ใช้ออกผ่านเมนู Refactor เมื่อคลิกขวา
NitinSingh

0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

ใช้ได้กับกรณีของฉันฉันใช้ ADFS Authentication

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.