首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >没有注册名为“Default”的IUserTwoFactorTokenProvider。AddDefaultTokenProviders()在两个(2) ASP.NET核心身份注册中的问题

没有注册名为“Default”的IUserTwoFactorTokenProvider。AddDefaultTokenProviders()在两个(2) ASP.NET核心身份注册中的问题
EN

Stack Overflow用户
提问于 2019-12-12 11:39:23
回答 3查看 16.5K关注 0票数 17

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

代码语言:javascript
运行
复制
1: CustomerContext : IdentityDbContext<CustomerUser>
2: ApplicationContext : IdentityDbContext<ApplicationUser>

我已经成功地在ASP.NET核心3.0API启动文件中注册了它们。一个使用'AddIdentity',另一个使用AddIdentity

我还将'AddDefaultTokenProviders'添加到了它们中。虽然它构建并运行,但当我尝试使用令牌提供程序(如GenerateEmailConfirmationTokenAsyncGeneratePasswordResetTokenAsync. )时,会出现问题。

如果我从注册中删除其中的一个“AddDefaultTokenProviders”,那么使用令牌对带有'AddDefaultTokenProviders‘的标识进行工作,如果两者都包括AddDefaultTokenProviders,则两者都不工作。我得到了这些例外(为了简洁起见,我对它们做了一些修改):

代码语言:javascript
运行
复制
System.NotSupportedException: No IUserTwoFactorTokenProvider named 'Default' is registered.
- at Microsoft.AspNetCore.Identity.UserManager.GenerateUserTokenAsync(GenerateEmailConfirmationTokenAsync)
OR
- at Microsoft.AspNetCore.Identity.UserManager.GenerateUserTokenAsync(GeneratePasswordResetTokenAsync)

以下是Startup.cs中的身份注册:

CustomerUser

代码语言:javascript
运行
复制
services.AddIdentity<CustomerUser, CustomerRole>(options =>
{
    options.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<CustomerContext>()
.AddDefaultTokenProviders(); // <-- CANNOT HAVE (2)

ApplicationUser

代码语言:javascript
运行
复制
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)

我偶然看到一篇文章提到IdentityOptions是单例的,不能两次调用AddDefaultTokenProviders。但没办法解决这个问题。

如何包括两个标识的默认令牌提供程序?是否需要创建自定义令牌提供程序?如果是这样的话,是怎么做的?我不需要任何令牌定制,我只需要默认的令牌行为。

谢谢。

EN

回答 3

Stack Overflow用户

发布于 2020-08-19 05:11:09

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

代码语言:javascript
运行
复制
services.AddIdentityCore<CustomerUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<TenantDataContext>()
                .AddTokenProvider<DataProtectorTokenProvider<CustomerUser>>(TokenOptions.DefaultProvider);

区别是按指示调用.AddTokenProvider而不是.AddDefaultTokenProviders()

票数 20
EN

Stack Overflow用户

发布于 2020-12-28 10:34:16

您应该添加令牌提供程序。

代码语言:javascript
运行
复制
public class PasswordResetTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
    {
        public PasswordResetTokenProvider(IDataProtectionProvider dataProtectionProvider,
            IOptions<PasswordResetTokenProviderOptions> options,
            ILogger<DataProtectorTokenProvider<TUser>> logger)
            : base(dataProtectionProvider, options, logger)
        {

        }
    }

    public class PasswordResetTokenProviderOptions : DataProtectionTokenProviderOptions
    {
        public PasswordResetTokenProviderOptions()
        {
            Name = "PasswordResetTokenProvider";
            TokenLifespan = TimeSpan.FromDays(3);
        }
    }

Startup.cs

代码语言:javascript
运行
复制
services.AddIdentity<AppTenantUser, AppTenantRole>(config =>
            {
                config.SignIn.RequireConfirmedEmail = true;
                config.Tokens.EmailConfirmationTokenProvider = "emailConfirmation";
                config.Tokens.PasswordResetTokenProvider = "passwordReset";
                config.Password.RequiredLength = 0;
                config.Password.RequiredUniqueChars = 0;
                config.Password.RequireLowercase = false;
                config.Password.RequireUppercase = false;
                config.Password.RequireDigit = false;
                config.Password.RequireNonAlphanumeric = false;
                config.User.RequireUniqueEmail = true;
                config.User.AllowedUserNameCharacters = "abcçdefghiıjklmnoöpqrsştuüvwxyzABCÇDEFGHIİJKLMNOÖPQRSŞTUÜVWXYZ0123456789-._@+'#!/^%{}*";
            })
                .AddEntityFrameworkStores<TenantDbContext>()
                .AddDefaultTokenProviders()
                .AddTokenProvider<EmailConfirmationTokenProvider<AppTenantUser>>("emailConfirmation")
                .AddTokenProvider<PasswordResetTokenProvider<AppTenantUser>>("passwordReset");
票数 8
EN

Stack Overflow用户

发布于 2020-02-26 20:05:36

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

AddDefaultTokenProviders

代码语言:javascript
运行
复制
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);
    }

AddTokenProvider

代码语言:javascript
运行
复制
 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;
        }

方法GenerateChangePhoneNumberTokenAsync使用

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

我做了什么,

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

CustomerUserManager

代码语言:javascript
运行
复制
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);
            }
    }

注意:我只在第一个标识中使用了AddDefaultTokenProviders

我不知道这是否是解决这个问题的最好办法,但它对我有效。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59303760

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档