问题描述
案例:我有一个启用了 Google 身份验证方法的 MVC5 应用程序(基本上是带有脚手架视图的 MVC5 模板).该应用程序已配置为接受电子邮件作为用户名,并将从 Google 分配的声明(如姓氏、名字、电子邮件、名称标识符等)存储到会员数据库 (AspNetUserClaims).
Case: I have an MVC5 application (basically the MVC5 template with a scaffolded view) with the Google authentication method enabled. The application has been configured to accept email as user name and to store the claims assigned from Google like Surname, givenname, email, nameidentifier, etc, to the membership database (AspNetUserClaims).
当我注册并使用本地"用户登录时,一切正常.如果我使用 Google 用户登录就可以了.如果我使用设置为本地和外部登录的帐户登录,则会收到以下错误.
When I register and log in with a "local" user everything is fine.If I log in with a Google user its fine.If I log in with an account set up to have both a local and external login I get the error below.
我尝试使用 Application_Start 中的 AntiForgeryConfig 选项将令牌的类型更改为不同的设置(示例)
I have tried changing the type for the token to different settings using the AntiForgeryConfig option in Application_Start (example)
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
但是当结合本地和外部登录时,似乎所有的声明都是重复的.最奇怪的是,对于组合登录和仅外部登录,声明集合(我认为有答案)是相同的.
But it seems all the Claims are duplicated when combining local and external logins. The strangest thing is that the claims-collection (which I assumed had the answer) is identical for the combined and the external only login.
当以本地用户身份登录时,这些声明被分配
When logged in as local user these Claims are assigned
[0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: a71ff9c0-8dc4-478b-a6f1-2c4cc34b1e46}
[1]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: [email protected]}
[2]: {http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider: ASP.NET Identity}
当使用仅远程或组合帐户登录时,声明列表如下所示
When logged in with a remote-only or a combined account the claims-list looks like this
[0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: 4ab33d77-c2a0-4eff-a759-5cca4323ecbf}
[1]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: [email protected]}
[2]: {http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider: ASP.NET Identity}
[3]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: https://www.google.com/accounts/o8/id?id=AitOGoogleIdentifierRemovedForPrivacygwgwgw}
[4]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: [email protected]}
[5]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname: Other}
[6]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname: Some}
[7]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: Some Other Person}
任何帮助将不胜感激!
Server Error in '/' Application.
Sequence contains more than one matching element
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Sequence contains more than one matching element
Source Error:
Line 10: @using (Html.BeginForm())
Line 11: {
Line 12: @Html.AntiForgeryToken()
Line 13:
Line 14: <div class="form-horizontal">
Source File: x:somewebViewssomeEntityCreate.cshtml Line: 12
Stack Trace:
[InvalidOperationException: Sequence contains more than one matching element]
System.Linq.Enumerable.SingleOrDefault(IEnumerable`1 source, Func`2 predicate) +2533810
System.Web.Helpers.AntiXsrf.ClaimUidExtractor.GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity, String uniqueClaimTypeIdentifier) +701
System.Web.Helpers.AntiXsrf.ClaimUidExtractor.ExtractClaimUid(IIdentity identity) +186
System.Web.Helpers.AntiXsrf.TokenValidator.GenerateFormToken(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken cookieToken) +242
System.Web.Helpers.AntiXsrf.AntiForgeryWorker.GetTokens(HttpContextBase httpContext, AntiForgeryToken oldCookieToken, AntiForgeryToken& newCookieToken, AntiForgeryToken& formToken) +174
System.Web.Helpers.AntiXsrf.AntiForgeryWorker.GetFormInputElement(HttpContextBase httpContext) +109
System.Web.Helpers.AntiForgery.GetHtml() +146
System.Web.Mvc.HtmlHelper.AntiForgeryToken() +39
ASP._Page_Views_Bruker_Create_cshtml.Execute() in x:prosjekterLaudiTFSLaudiIWebInspectorInspectorViewsBrukerCreate.cshtml:12
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +271
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +120
System.Web.WebPages.StartPage.RunPage() +63
System.Web.WebPages.StartPage.ExecutePageHierarchy() +100
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +131
System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +695
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +382
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +431
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +116
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +529
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +106
System.Web.Mvc.Async.<>c__DisplayClass28.<begininvokeaction>
b__19() +321
System.Web.Mvc.Async.<>c__DisplayClass1e.<begininvokeaction>
b__1b(IAsyncResult asyncResult) +185
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +40
System.Web.Mvc.Controller.<beginexecutecore>
b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +34
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44
System.Web.Mvc.Controller.<beginexecute>
b__15(IAsyncResult asyncResult, Controller controller) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +62
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.MvcHandler.<beginprocessrequest>
b__4(IAsyncResult asyncResult, ProcessRequestState innerState) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9688704
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
推荐答案
行为正常,从下面的方法(创建用户身份时调用)可以看出:
The behavior is normal as you can see from the following method (called when creating the user identity):
await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)
在 SignInAsync
方法中.在CreateIdentityAsync
的实现中:
in SignInAsync
method.In the implementation of CreateIdentityAsync
:
...
ClaimsIdentity claimsIdentity = new ClaimsIdentity(authenticationType, this.UserNameClaimType, this.RoleClaimType);
claimsIdentity.AddClaim(new Claim(this.UserIdClaimType, user.Id, "http://www.w3.org/2001/XMLSchema#string"));
claimsIdentity.AddClaim(new Claim(this.UserNameClaimType, user.UserName, "http://www.w3.org/2001/XMLSchema#string"));
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"));
if (manager.SupportsUserRole)
{
foreach (string rolesAsync in await manager.GetRolesAsync(user.Id))
{
claimsIdentity.AddClaim(new Claim(this.RoleClaimType, rolesAsync, "http://www.w3.org/2001/XMLSchema#string"));
}
}
if (manager.SupportsUserClaim)
{
claimsIdentity.AddClaims(await manager.GetClaimsAsync(user.Id));
}
...
如您所见,默认添加了三个声明.他们添加了您的自定义"声明.这就是为什么您会有重复的声明,这意味着对声明集合的 SingleOrDefault
调用将抛出您提到的错误.作为解决方案,您可以使用其他声明或更新它们,在创建身份后,这取决于您的业务需要.
As you can see there are three claims added by default. To them are added your "custom" claims. This is why you'll have duplicated claims, meaning that SingleOrDefault
call on the claims collection will throw the error that you've mentioned.As a solution you can either use other claims either update them, after the identity creation, it's up to your business need.
这篇关于MVC5 AntiForgeryToken Claims/“序列包含多个元素"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!