没有注册名为“默认”的 IUserTwoFactorTokenProvider。问题是两 (2) 个 ASP.NET Core 身份注册中的 AddDefaultTokenProviders()

Ron*_*nie 11 c# asp.net-web-api asp.net-identity asp.net-core

我有 (2) Identity systems,每个都有自己的Context

1: CustomerContext : IdentityDbContext<CustomerUser>
2: ApplicationContext : IdentityDbContext<ApplicationUser>
Run Code Online (Sandbox Code Playgroud)

我已经在 ASP.NET Core 3.0 API 启动文件中成功注册了它们。一个使用“AddIdentity”,另一个使用“AddIdentityCore”

我还向它们添加了“AddDefaultTokenProviders”。尽管它构建并运行,但当我尝试使用令牌提供程序时会出现问题,例如GenerateEmailConfirmationTokenAsyncGeneratePasswordResetTokenAsync

如果我从注册中删除“AddDefaultTokenProviders”之一,那么使用令牌适用于带有“AddDefaultTokenProviders”的身份,当两者都包含 AddDefaultTokenProviders 时,两者都不起作用。我得到了这些例外(为简洁起见,我对它们进行了一些修剪):

System.NotSupportedException: No IUserTwoFactorTokenProvider named 'Default' is registered.
- at Microsoft.AspNetCore.Identity.UserManager.GenerateUserTokenAsync(GenerateEmailConfirmationTokenAsync)
OR
- at Microsoft.AspNetCore.Identity.UserManager.GenerateUserTokenAsync(GeneratePasswordResetTokenAsync)
Run Code Online (Sandbox Code Playgroud)

这些是 Startup.cs 中的身份注册:

客户用户

services.AddIdentity<CustomerUser, CustomerRole>(options =>
{
    options.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<CustomerContext>()
.AddDefaultTokenProviders(); // <-- CANNOT HAVE (2)
Run Code Online (Sandbox Code Playgroud)

应用用户

var builder = services.AddIdentityCore<ApplicationUser>(options =>
{
    options.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(ApplicationRole), builder.Services);
builder.AddEntityFrameworkStores<ApplicationContext>();
builder.AddDefaultTokenProviders(); // <-- CANNOT HAVE (2)
Run Code Online (Sandbox Code Playgroud)

我看到一篇文章提到 IdentityOptions 是单例的,不能两次调用 AddDefaultTokenProviders。但没有解决如何解决它。

如何为两个身份包含默认令牌提供程序?我需要创建自定义令牌提供程序吗?如果是这样,如何?我不需要任何令牌定制,我只需要默认的令牌行为。

谢谢你。

Sha*_*Wet 9

我通过添加第二个身份服务解决了这个问题,如下所示:

services.AddIdentityCore<CustomerUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<TenantDataContext>()
                .AddTokenProvider<DataProtectorTokenProvider<CustomerUser>>(TokenOptions.DefaultProvider);
Run Code Online (Sandbox Code Playgroud)

不同之处在于.AddTokenProvider按指示调用而不是.AddDefaultTokenProviders()


cod*_*ver 9

您应该添加令牌提供者。

\n
public class PasswordResetTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class\n    {\n        public PasswordResetTokenProvider(IDataProtectionProvider dataProtectionProvider,\n            IOptions<PasswordResetTokenProviderOptions> options,\n            ILogger<DataProtectorTokenProvider<TUser>> logger)\n            : base(dataProtectionProvider, options, logger)\n        {\n\n        }\n    }\n\n    public class PasswordResetTokenProviderOptions : DataProtectionTokenProviderOptions\n    {\n        public PasswordResetTokenProviderOptions()\n        {\n            Name = "PasswordResetTokenProvider";\n            TokenLifespan = TimeSpan.FromDays(3);\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

启动.cs

\n
services.AddIdentity<AppTenantUser, AppTenantRole>(config =>\n            {\n                config.SignIn.RequireConfirmedEmail = true;\n                config.Tokens.EmailConfirmationTokenProvider = "emailConfirmation";\n                config.Tokens.PasswordResetTokenProvider = "passwordReset";\n                config.Password.RequiredLength = 0;\n                config.Password.RequiredUniqueChars = 0;\n                config.Password.RequireLowercase = false;\n                config.Password.RequireUppercase = false;\n                config.Password.RequireDigit = false;\n                config.Password.RequireNonAlphanumeric = false;\n                config.User.RequireUniqueEmail = true;\n                config.User.AllowedUserNameCharacters = "abc\xc3\xa7defghi\xc4\xb1jklmno\xc3\xb6pqrs\xc5\x9ftu\xc3\xbcvwxyzABC\xc3\x87DEFGHI\xc4\xb0JKLMNO\xc3\x96PQRS\xc5\x9eTU\xc3\x9cVWXYZ0123456789-._@+'#!/^%{}*";\n            })\n                .AddEntityFrameworkStores<TenantDbContext>()\n                .AddDefaultTokenProviders()\n                .AddTokenProvider<EmailConfirmationTokenProvider<AppTenantUser>>("emailConfirmation")\n                .AddTokenProvider<PasswordResetTokenProvider<AppTenantUser>>("passwordReset");\n
Run Code Online (Sandbox Code Playgroud)\n


Ibr*_*med 7

我遇到了同样的问题,在抛出源代码之后,我意识到该AddDefaultTokenProviders方法AddTokenProvider调用配置IdentityOptions并覆盖默认令牌提供程序。

添加默认令牌提供商

public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder)
        {
            var userType = builder.UserType;
            var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType);
            var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType);
            var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType);
            var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType);
            return builder.AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType)
                .AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType)
                .AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType)
                .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType);
    }
Run Code Online (Sandbox Code Playgroud)

添加令牌提供者

 public virtual IdentityBuilder AddTokenProvider(string providerName, Type provider)
        {
            if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo()))
            {
                throw new InvalidOperationException(Resources.FormatInvalidManagerType(provider.Name, "IUserTwoFactorTokenProvider", UserType.Name));
            }
            Services.Configure<IdentityOptions>(options =>
            {
                options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider);
            });
            Services.AddTransient(provider);
            return this;
        }
Run Code Online (Sandbox Code Playgroud)

并且该方法GenerateChangePhoneNumberTokenAsync使用了
IOptions<IdentityOptions>在构造函数中注入的which UserManager ,因此我们不能使用该方法为多个身份生成令牌。

我做了什么

我在被UserManager调用的方法中使用了一个方法RegisterTokenProvider来注册我的令牌提供程序,并在我的用户管理器中创建了一个新方法来生成令牌,在我调用的该方法中,GenerateUserTokenAsync该方法将 tokenProvider 作为参数。

客户用户经理

public class CustomerUserManager: UserManager<CustomerUser>

    {
    public CustomerUserManager(IUserStore<CustomerUser> store, IOptions<CustomerIdentityOptions> optionsAccessor,
           IPasswordHasher<CustomerUser> passwordHasher, IEnumerable<IUserValidator<CustomerUser>> userValidators,
           IEnumerable<IPasswordValidator<CustomerUser>> passwordValidators, ILookupNormalizer keyNormalizer,
           IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<GlameraUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
            {

                RegisterTokenProvider(TokenOptions.DefaultPhoneProvider, new PhoneNumberTokenProvider<CustomerUser>());
            }
            public virtual Task<string> CustomChangePhoneNumberTokenAsync(CustomerUser user, string phoneNumber)
            {
                ThrowIfDisposed();
                return GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, ChangePhoneNumberTokenPurpose + ":" + phoneNumber);
            }
    }
Run Code Online (Sandbox Code Playgroud)

注意:AddDefaultTokenProviders我仅在第一个身份中使用。

我不确定这是否是解决此问题的最佳方法,但它对我有用。