用户在 ASP.NET Core Identity 中打开设置页面时如何询问密码?
Posted
技术标签:
【中文标题】用户在 ASP.NET Core Identity 中打开设置页面时如何询问密码?【英文标题】:How to ask for password when user open settings page in ASP.NET Core Identity? 【发布时间】:2021-09-02 00:43:57 【问题描述】:我在 ASP.NET Core 3.1 中配置了 Web 应用程序以将 ASP.NET Core Identity 与本地帐户一起使用。 一切正常。 我想强制用户在转到特定页面时再次插入密码(或使用外部提供程序),例如帐号设定。 我找不到这个案例的例子。 有人有这方面的经验吗? 也许有些文章我错过了。
到目前为止,唯一的想法是注销用户并打开登录页面,但这不符合逻辑,因为他应该能够不受限制地打开其他页面。
【问题讨论】:
【参考方案1】:我没有尝试过,但我偶然发现了这个 blog post,听起来它对你有用,我已经把它缩短到几乎 TL;DR 帖子
设置所需的密码验证页面
RequirePasswordVerificationModel 类实现了 Razor 页面,该页面要求用户在过去十分钟内验证了身份用户的密码。
public class RequirePasswordVerificationModel : PasswordVerificationBase
public RequirePasswordVerificationModel(UserManager<ApplicationUser> userManager) : base(userManager)
public async Task<IActionResult> OnGetAsync()
var passwordVerificationOk = await ValidatePasswordVerification();
if (!passwordVerificationOk)
return RedirectToPage("/PasswordVerification",
new ReturnUrl = "/DoUserChecks/RequirePasswordVerification" );
return Page();
PasswordVerificationBase Razor 页面实现了 PageModel。 ValidatePasswordVerification 方法检查用户是否已经通过身份验证
public class PasswordVerificationBase : PageModel
public static string PasswordCheckedClaimType = "passwordChecked";
private readonly UserManager<ApplicationUser> _userManager;
public PasswordVerificationBase(UserManager<ApplicationUser> userManager)
_userManager = userManager;
public async Task<bool> ValidatePasswordVerification()
if (User.Identity.IsAuthenticated)
if (User.HasClaim(c => c.Type == PasswordCheckedClaimType))
var user = await _userManager.FindByEmailAsync(User.Identity.Name);
var lastLogin = DateTime.FromFileTimeUtc(
Convert.ToInt64(user.LastLogin));
var lastPasswordVerificationClaim
= User.FindFirst(PasswordCheckedClaimType);
var lastPasswordVerification = DateTime.FromFileTimeUtc(
Convert.ToInt64(lastPasswordVerificationClaim.Value));
if (lastLogin > lastPasswordVerification)
return false;
else if (DateTime.UtcNow.AddMinutes(-10.0) > lastPasswordVerification)
return false;
return true;
return false;
如果用户需要重新输入凭据,则使用 PasswordVerificationModel Razor 页面。此类是使用 ASP.NET Core Identity 中的标识脚手架登录 Razor 页面构建的。使用 UserManager 服务删除旧的密码验证声明。如果用户成功重新输入密码并且使用新的 ClaimIdentity 实例刷新登录,则会创建新的密码验证声明。
public class PasswordVerificationModel : PageModel
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<PasswordVerificationModel> _logger;
public PasswordVerificationModel(SignInManager<ApplicationUser> signInManager,
ILogger<PasswordVerificationModel> logger,
UserManager<ApplicationUser> userManager)
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
[BindProperty]
public CheckModel Input get; set;
public IList<AuthenticationScheme> ExternalLogins get; set;
public string ReturnUrl get; set;
[TempData]
public string ErrorMessage get; set;
public class CheckModel
[Required]
[DataType(DataType.Password)]
public string Password get; set;
public async Task<IActionResult> OnGetAsync(string returnUrl = null)
if (!string.IsNullOrEmpty(ErrorMessage))
ModelState.AddModelError(string.Empty, ErrorMessage);
var user = await _userManager.GetUserAsync(User);
if (user == null)
return NotFound($"Unable to load user with ID '_userManager.GetUserId(User)'.");
var hasPassword = await _userManager.HasPasswordAsync(user);
if (!hasPassword)
return NotFound($"User has no password'_userManager.GetUserId(User)'.");
returnUrl ??= Url.Content("~/");
ReturnUrl = returnUrl;
return Page();
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
returnUrl ??= Url.Content("~/");
var user = await _userManager.GetUserAsync(User);
if (user == null)
return NotFound($"Unable to load user with ID '_userManager.GetUserId(User)'.");
if (ModelState.IsValid)
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(user.Email, Input.Password, false, lockoutOnFailure: false);
if (result.Succeeded)
_logger.LogInformation("User password re-entered");
await RemovePasswordCheck(user);
var claim = new Claim(PasswordVerificationBase.PasswordCheckedClaimType,
DateTime.UtcNow.ToFileTimeUtc().ToString());
await _userManager.AddClaimAsync(user, claim);
await _signInManager.RefreshSignInAsync(user);
return LocalRedirect(returnUrl);
if (result.IsLockedOut)
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
else
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
// If we got this far, something failed, redisplay form
return Page();
private async Task RemovePasswordCheck(ApplicationUser user)
if (User.HasClaim(c => c.Type == PasswordVerificationBase.PasswordCheckedClaimType))
var claims = User.FindAll(PasswordVerificationBase.PasswordCheckedClaimType);
foreach (Claim c in claims)
await _userManager.RemoveClaimAsync(user, c);
PasswordVerificationModel Razor 页面 html 模板显示带有密码字段的用户输入表单。
@page
@model PasswordVerificationModel
@
ViewData["Title"] = "Password Verification";
<h1>@ViewData["Title"]</h1>
<div class="row">
<div class="col-md-4">
<section>
<form id="account" method="post">
<h4>Verify account using your password</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password"
class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">
Re-enter password
</button>
</div>
</form>
</section>
</div>
</div>
@section Scripts
<partial name="_ValidationScriptsPartial" />
登录 Razor 页面需要更新,以便在登录成功时为 DateTime.UtcNow 添加登录文件时间值。此值在基本 Razor 页面中用于验证密码检查。为此添加了 LastLogin 属性。
var result = await _signInManager.PasswordSignInAsync(
Input.Email, Input.Password,
Input.RememberMe,
lockoutOnFailure: false);
if (result.Succeeded)
_logger.LogInformation("User logged in.");
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
return NotFound("help....");
user.LastLogin = DateTime.UtcNow.ToFileTimeUtc().ToString();
var lastLoginResult = await _userManager.UpdateAsync(user);
return LocalRedirect(returnUrl);
LastLogin 属性已添加到实现 IdentityUser 的 ApplicationUser。该值被持久化到 Entity Framework Core 数据库中。
public class ApplicationUser : IdentityUser
public string LastLogin get; set;
当应用程序启动时,用户可以登录,并且需要验证密码才能访问实现需要此功能的 Razor 页面。
【讨论】:
以上是关于用户在 ASP.NET Core Identity 中打开设置页面时如何询问密码?的主要内容,如果未能解决你的问题,请参考以下文章
在 ASP.Net Core Identity 中刷新用户 cookie 票证
asp.net core系列 48 Identity 身份模型自定义
使用 ASP.NET Core Identity 3 的用户角色权限