Ant*_*hog 7 c# asp.net-mvc asp.net-core
我正在开发一个 asp.net core mvc 项目(.net 5),但在与身份相关的事情上苦苦挣扎。
我有一个HedgehogUserAccount继承自: andIdentityUser的另外两个类(名称选择错误,我知道 - 当一切正常时我会更改它)。经过大量工作后,我成功地使迁移工作并且程序编译并运行,但是当我尝试注册 a 时,出现以下错误:HedgehogUserAccountCustomerAccountUserAccountUser
NotSupportedException: No IUserTwoFactorTokenProvider<TUser> named 'Default' is registered.
该错误来自我的注册代码中的第 90 行(请参阅下面粘贴的完整代码 - 它与脚手架注册页面几乎相同):
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
我的 Startup.cs 的相关(我认为)部分如下所示:
services.AddIdentity<HedgehogUserAccount, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
services.AddIdentityCore<UserAccount>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
services.AddIdentityCore<CustomerAccount>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
Run Code Online (Sandbox Code Playgroud)
我在尝试注册我的UserManager课程时得到了这个代码。
我尝试过一些变体(替换)AddDefaultTokenProviders:
.AddTokenProvider(TokenOptions.DefaultEmailProvider, typeof(EmailTokenProvider<UserAccount>)).AddTokenProvider<EmailTokenProvider<UserAccount>>(TokenOptions.DefaultEmailProvider)但它们都在我的代码中的同一点产生相同的错误。但当我尝试时,我收到了一条有趣的消息:
services.AddIdentityCore<UserAccount>(
options => {
options.Tokens.ProviderMap.Add("Default", new
TokenProviderDescriptor(typeof(IUserTwoFactorTokenProvider<UserAccount>)));
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
Run Code Online (Sandbox Code Playgroud)
然后我收到的错误消息是:System.ArgumentException: 'An item with the same key has already been added. Key: Default'这感觉有点奇怪,因为上一条消息说不Default存在。
我想我必须做的是以某种方式提供我自己的EmailTokenProvider?或者还有其他办法吗?我非常感谢您的帮助!
更新
我尝试用以下行替换生成确认令牌的行:
var code = "testtoken";//await _userManager.GenerateEmailConfirmationTokenAsync(user);
当我这样做时,程序和注册运行完美!我想这意味着默认令牌提供程序未正确注册,但我不确定如何继续。
Register.cshtml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Hedgehog.Core.Application;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
namespace Hedgehog.UI.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<UserAccount> _signInManager;
private readonly UserManager<UserAccount> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<UserAccount> userManager,
RoleManager<IdentityRole> roleManager,
SignInManager<UserAccount> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_roleManager = roleManager;
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new UserAccount { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
_userManager.AddToRoleAsync(user, "User").Wait();
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
}
Run Code Online (Sandbox Code Playgroud)
对我有用的解决方案是:仅
添加.AddDefaultTokenProviders();到您的 IdentityDbContext 之一,然后在其他 2 个上,创建一个继承自 UserManage 的自定义 UserManager,然后在构造函数中添加以下行
RegisterTokenProvider(TokenOptions.DefaultProvider, new EmailTokenProvider<Owner>());
Run Code Online (Sandbox Code Playgroud)
此行为 UserManager 注册一个新的 EmailTokenProvider。
经过一番头痛之后,我现在已经对我的问题得出了结论。我通过实现自己的令牌提供程序并将其注册到系统来解决这个问题。万一其他人和我一样撞到同一堵墙上,我把我的解决方案留在这里。
第 1 步:实现 IUserTwoFactorTokenProvider
public class HedgehogEmailTwoFactorAuthentication<TUser> : IUserTwoFactorTokenProvider<TUser> where TUser : HedgehogUserAccount
{
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
{
if (manager != null && user != null)
{
return Task.FromResult(true);
}
else
{
return Task.FromResult(false);
}
}
// Genereates a simple token based on the user id, email and another string.
private string GenerateToken(HedgehogUserAccount user, string purpose)
{
string secretString = "coffeIsGood";
return secretString + user.Email + purpose + user.Id;
}
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
{
return Task.FromResult(GenerateToken(user, purpose));
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
{
return Task.FromResult(token == GenerateToken(user, purpose));
}
}
Run Code Online (Sandbox Code Playgroud)
第 2 步:在服务中注册您的新的闪亮令牌提供商
services.AddIdentityCore<CustomerAccount>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddTokenProvider("Default", typeof(HedgehogEmailTwoFactorAuthentication<CustomerAccount>))
.AddDefaultUI();
Run Code Online (Sandbox Code Playgroud)
步骤3:向DI容器注册
在ConfigureContainer中(我使用Autofac):
builder.RegisterType<HedgehogEmailTwoFactorAuthentication<CustomerAccount>>()
.As<IUserTwoFactorTokenProvider<CustomerAccount>>()
.InstancePerLifetimeScope();
Run Code Online (Sandbox Code Playgroud)
如果您不使用 Autofac,则需要调用AddScoped而不是InstancePerLifetimeScope(假设这是令牌生成器的正确生命周期)。
另外,您可能希望为您的实现使用更好的令牌生成器:)
参考:微软文档
| 归档时间: |
|
| 查看次数: |
6031 次 |
| 最近记录: |