没有注册名为 'Default' 的 IUserTwoFactorTokenProvider。问题是两 (2) 个核心标识注册中的 AddDefaultTokenProviders() ASP.N



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

1: CustomerContext : IdentityDbContext<CustomerUser>
2: ApplicationContext : IdentityDbContext<ApplicationUser>

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

我还为它们添加了"AddDefaultTokenProviders"。虽然它会生成并运行,但当我尝试使用令牌提供程序(如GenerateEmailConfirmationTokenAsync 或 GeneratePasswordResetTokenAsync(时,会出现问题。

如果我从注册中删除其中一个"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)

这些是 Startup.cs 中的标识注册:

客户用户

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

应用程序用户

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两次。但没有解决如何解决它。

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

谢谢。

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

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

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

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

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);
}
}

启动.cs

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");

我遇到了同样的问题,在抛出源代码后,我意识到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);
}

添加令牌提供程序

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 作为参数。

客户用户管理器

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

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

相关内容

  • 没有找到相关文章

最新更新