反伪造令牌发行(MVC 5)


122

我在使用防伪令牌时遇到了问题:(我创建了自己的User类,该类工作正常,但是现在每当进入/ Account / Register页面时,都会出现错误。错误是:

类型为“ 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;
}

但是当我这样做的时候,我得到了这个错误:

提供的ClaimsIdentity上没有类型为http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress的声明。

有人遇到过吗?如果是这样,您知道如何解决吗?

提前
加油,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 2013年

我添加了自定义User类,以及我的使用方式。
r3plica

您正在使用测试版。我建议您升级到发行版本,然后查看问题是否仍然存在。
LostInComputer 2013年

Answers:


230

尝试设置(在global.cs中):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

33
我认为重要的是要注意这是为什么工作的:这告诉AntiForgery该类使用NameIdentifier(这是通过查找的用户ID字符串GetUserId)。感谢Mike Goodwin的回答,帮助我学习了!
马特·德克里

我尝试了“ AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;” 并收到此错误“顺序包含多个匹配元素”,在我的情况下,有多个声明(名称,角色和电子邮件地址)。我该如何解决呢?
Dhanuka777

9
我在Global.asax.cs中设置了此设置
Mike Taverne,2015年

6
如果您使用OpenId(即Azure ActiveDirectory)作为身份验证,这也是解决方案。
guysherman

6
完整的名称空间..我不得不做一些挖掘来找出ClaimTypes的存放位置。System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
马克罗

65

您知道您在ClaimsIdentity中得到什么索赔吗?如果不:

  1. 删除[ValidateAntiForgeryToken]属性
  2. 在控制器中的某个地方放置一个断点,然后中断它
  3. 然后查看当前内容ClaimsIdentity并检查索赔
  4. 查找一个您认为可以唯一标识您的用户的人
  5. 将设置AntiForgeryConfig.UniqueClaimTypeIdentifier为该声明类型
  6. 放回[ValidateAntiForgeryToken]属性

3
除了提供直接的汤匙喂食答案外,该还可以告诉背景并实现自我发现。:)非常感谢
NitinSingh '18

2
6.放回[ValidateAntiForgeryToken]属性
Scott Fraley

1
这真的帮助了我。事实证明,我在本地没有运行任何声明的应用程序中在本地主机上运行的另一个应用程序提出了声明(这就是为什么声明声明听起来很奇怪)的原因。因此,当我退出另一个应用程序时,声明消失了,错误也消失了。在实时测试环境中,这些站点更加分离。因此,我认为我需要上述解决方案,但仅适用于本地开发。
米歇尔

26

只需将其放在global.asax.cs中

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

谢谢。我没有得到的是为什么我必须进行更改,我修复了昨晚我的代码遇到的一些其他问题,并且一切正常。无需进行任何更改,我在另一台机器上进行了测试,直到几分钟前,一切都可以正常工作。
Artorias2718 '19

14

尝试在隐身窗口中打开链接或清除该域(即localhost)中的cookie。


为什么这样做有效,问题的原因是什么?
莫克(Mok)2016年

之所以可行,是因为当您拥有带有无效nameidentifier的会话cookie时,服务器会尝试使用无效标识符,而不将用户重定向到登录页面并获取正确的nameidentifier。
rawel

3

编辑:此刻对此问题有了更多的了解,您可以在下面忽略我的答案。

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;Global.asax.cs的Application_Start()中的设置已为我修复。即使我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 '16

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 '18

感谢您添加用法。@NitinSingh我认为这可以增加更多的价值,因为我不知道要使用项目中三个潜在名称空间中的哪个。
Keisha W

每当您添加新功能时,它将要求正确的参考。一旦编译,你应该通过在右键菜单中重构删除未使用的
NitinSingh

0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

适用于我的情况,我正在使用ADFS身份验证。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.