身份服务器和 AspNetUsers

identitysever and AspNetUsers

我有一个设置

services.AddIdentity<AppUser, AppRole>() 
    .AddEntityFrameworkStores<ApplicationDbContext>() 
    .AddUserManager<userManage>()
    .AddDefaultTokenProviders();

其中使用了 AppUserAppRole,但似乎因此失败了。我不断收到

ArgumentNullException: Value cannot be null. Parameter name: type for the claims at System.Security.Claims.Claim..ctor

之后

Microsoft.AspNetCore.Identity.IdentityUserClaim1.ToClaim()

底部整条日志

在我为 IdentityUserIdentityRole

引入扩展之前一切正常

对于 IDS4 设置,我有:

        services.AddIdentityServer(options => {
                options.UserInteraction.LoginUrl = "/Account/Login";
            })
           .AddSigningCredential(new X509Certificate2(Path.Combine(".", "certs"
                                                                 , "IdentityServer4Auth.pfx")))
           .AddAspNetIdentity<AppUser>()
           .AddConfigurationStore(options => {
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connection_string, sql => sql.MigrationsAssembly(migrations_assembly));
            })
           .AddOperationalStore(options => {
                //options.DefaultSchema = "token";
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connection_string, sql => sql.MigrationsAssembly(migrations_assembly));
            })
           .AddInMemoryApiResources(Config.GetApiResources())
           .AddInMemoryIdentityResources(Config.GetIdentityResources())
           .AddJwtBearerClientAuthentication()
           .AddProfileService<IdentityProfileService>();

工作正常,但是从

切换
         services.AddIdentity<ApplicationUser, IdentityRole>(options =>
        {  })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
        services.AddDistributedMemoryCache();

        services.AddIdentity<AppUser, AppRole>()
           .AddEntityFrameworkStores<ApplicationDbContext>()
           .AddUserManager<userManage>()
           .AddDefaultTokenProviders();

现在就结束了。我知道只要设置 AppUserAppRoleuserManage 就可以正常工作,因为它们与我的许多应用程序中使用的设置相同,但一旦与IDS4 现在失败了。当它工作时,我扩展了 IdentityUser

public class ApplicationUser : IdentityUser {}

它们也在工作,当我将这两个应用程序混合在一起时,它们都出现了 AppUserAppRoleuserManage,这是它变坏的时候。 模型我会放在下面

旁注,我已经关注了日志,这里让我感到沮丧的是数据库中的查询 运行 是正确的。当我 运行 它时,我看不到任何类型值的空值。为了安全起见,我什至清除了数据库中任何声明区域中的任何空值,例如角色或用户级别。我已经在导致故障的区域设置了断点,

    var signin_result = await _signInManager.PasswordSignInAsync(_user, test, model.RememberMe, false);

当我查看 _user 时,我看到它有安全标记并且所有值都正确填充

在用户声明中

SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = @__user_Id_0

但我看不出问题出在哪里 returns 空值

日志

2018-10-08 13:17:47.164 -07:00 [Information] Entity Framework Core "2.1.4-rtm-31024" initialized '"ApplicationDbContext"' using provider '"Microsoft.EntityFrameworkCore.SqlServer"' with options: "SensitiveDataLoggingEnabled "
2018-10-08 13:17:47.189 -07:00 [Information] Executed DbCommand ("1"ms) [Parameters=["@__user_Id_0='11325643' (Size = 450)"], CommandType='Text', CommandTimeout='30']"
""SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = @__user_Id_0"
2018-10-08 13:17:47.370 -07:00 [Error] An exception occurred in the database while iterating the results of a query for context type '"test.app.Data.ApplicationDbContext"'."
""System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value)
at Microsoft.AspNetCore.Identity.IdentityUserClaim`1.ToClaim()
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper<TOut>.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)"
System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value)
at Microsoft.AspNetCore.Identity.IdentityUserClaim`1.ToClaim()
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper`3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper<TOut>.Shape(QueryContext queryContext, ValueBuffer& valueBuffer)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)
2018-10-08 13:17:48.680 -07:00 [Information] Executed action "WSU.Sso.Controllers.AccountController.Login (test.app)" in 2022.4589ms

模型和设置(在 none IDS4 应用程序中注意这些工作正常)

public class AppUser : IdentityUser {}
public class AppRole : IdentityRole {}
public class AppUserClaim : IdentityUserClaim<string> {}
public class AppUserRole : IdentityUserRole<string> {}
public class AppRoleClaims : IdentityRoleClaim<string> {}
public partial class CoreDbContext : IdentityDbContext
<
    AppUser, // TUser
    AppRole, // TRole
    string, // TKey
    AppUserClaim, // TUserClaim
    AppUserRole, // TUserRole,
    IdentityUserLogin<string>, // TUserLogin
    AppRoleClaims, // TRoleClaim
    IdentityUserToken<string> // TUserToken
>//, ICoreDbContext
{
    //etc.. 
}

更新

我认为问题出在这里,https://github.com/IdentityServer/IdentityServer4.AspNetIdentity/blob/dev/src/UserClaimsFactory.cs 其中 UserManager<TUser> userManager 需要是 userManage 因为我扩展了它。我猜我似乎需要扮演自己的角色?

更新 2

事实证明,在删除扩展 UserManager<TUser>userManage 之后,它仍然失败

System.Security.Claims.Claim..ctor(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject, string propertyKey, string propertyValue) System.Security.Claims.Claim..ctor(string type, string value) Microsoft.AspNetCore.Identity.IdentityUserClaim.ToClaim()

曾希望简化它成为答案,所以我不必重写 .AddAspNetIdentity<AppUser>() 但这没有用。很明显,它不是来自 DB 的东西是空的,但必须是添加的东西,它的值是空的。我不知道是什么,只是它必须在 Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ToClaim()IdentityServer4.AspNetIdentity.UserClaimsFactory<TUser>.CreateAsync(TUser user) in UserClaimsFactory.cs 之间......我可以肯定地说的是 https://github.com/IdentityServer/IdentityServer4.AspNetIdentity/blob/dev/src/UserClaimsFactory.cs 中的所有部分都是尝试设置在数据库中验证为不为空。

在我添加自定义角色之前 IDS4 正在运行也是毫无意义的。我不是 100% 确定这还没有在这里发挥作用。

旁注

所以我想知道,正如我排除的那样,这里的问题是否是 ID 的名称。在更新中,AspNetUser 的 tables 将 table 设置为 user_id 而不是 Id 是让我思考这个问题的原因)。话虽如此,我相信设置没有任何问题,这是我 OnModelCreating(ModelBuilder builder)

中的内容
builder.Entity<AppUser>().Property(p => p.Id).HasColumnName("user_id");

到目前为止,我们在使用它时没有遇到任何问题,但我正在运行消除用户中可能成为问题根源的所有差异。

更新 3

单步执行 public class IdentityUserClaim<TKey> where TKey : IEquatable<TKey> 并在 Claim ToClaim() 上设置断点后,类型为 null。这在日志上是一个 duh,但我似乎无法在调用堆栈中回溯它的根源。我的问题是,如果数据库查询 returns 一组正确的类型和值,那么为什么首先要处理空类型和值集?第一次命中断点时,类型为 null。

更新 4 个主要停靠点

在遇到每个断点后,如果我在这里遇到错误,我现在会被卡住并且受伤。我进入 UserManager 并跟随堆栈,可以看到主要的声明就在那里并且没问题。

然后下一部分是 运行 执行上述查询的声明存储,此时是它失败的时候。我不明白为什么。我 运行 在 SQL 管理器中查询,没问题,没有一个 NULL

有人看到这是怎么回事吗?我现在很茫然

更新 5 - 缩小范围

所以当我点击被设置为 null 的声明时没有注意到当地人,我看到我想要的值在 this

的范围内

现在的问题是范围如何关闭,我在错误的地方得到了想要的值

这个问题花了一些时间来解决,但现在很清楚为什么会失败。 AppUserClaim 才是真正的问题。很难说,在那一步看着当地人,这就是为什么我不得不在这里走很长的路。

我的扩展IdentityUserClaim<string>

public class AppUserClaim : IdentityUserClaim<string>
{
    public int Id { get; set; }

    /// <summary>
    /// Gets or sets the primary key of the user associated with this claim.
    /// </summary>
    public virtual string UserId { get; set; }

    public virtual AppUser user { get; set; }

    /// <summary>
    /// Gets or sets the claim type for this claim.
    /// </summary>
    public virtual string ClaimType { get; set; }

    /// <summary>
    /// Gets or sets the claim value for this claim.
    /// </summary>
    public virtual string ClaimValue { get; set; }

}

但是ClaimTypeClaimValue的属性不需要被覆盖。一旦我这样做了,它就会在它们超出方法范围的地方设置值。将模型更改为

public class AppUserClaim : IdentityUserClaim<string>  {
    public virtual AppUser user { get; set; } // note this is the reason why i had to extend

}

问题已解决。它没有出现在其他应用程序中,因为声明从 SSO 设置的位置,所以该方法总是被跳过。

我要记住的教训是,在经过时一定要看看当地人。我花了一天的时间才看到那个 duh 时刻。