You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core托管Blazor应用自定义密码邮箱重置实现咨询

实现方案

问题原因

你之前访问默认重置页面报错的原因是:脚手架生成的ResetPasswordModel是为「用户点击邮件重置链接」的场景设计的,必须携带Identity生成的加密重置token(也就是提示里的code)才能正常加载,不适合无邮件流程的自定义需求,不需要硬改原有文件,新增自定义Razor页面实现更简单。

前置安全要求

  • 必须给重置页面加权限控制:如果是管理员给其他用户重置,加[Authorize(Roles = "Admin")]特性;如果是登录用户改自己的账号,加[Authorize]特性,绝对不能开放匿名访问。

实现步骤

1. 新增自定义Razor页面

在Areas/Identity/Pages/Account目录下新增两个文件:CustomReset.cshtml和CustomReset.cshtml.cs

2. 编写PageModel后端逻辑

CustomReset.cshtml.cs示例代码:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;

// 替换为你自己的项目命名空间
namespace YourAppNamespace.Areas.Identity.Pages.Account
{
    // 按需修改权限特性,管理员使用就加Roles参数
    [Authorize(Roles = "Admin")]
    public class CustomResetModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;

        public CustomResetModel(UserManager<IdentityUser> userManager)
        {
            _userManager = userManager;
        }

        [BindProperty]
        public InputModel Input { get; set; }

        public class InputModel
        {
            [Required]
            [EmailAddress]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "密码长度至少6位", MinimumLength = 6)]
            [DataType(DataType.Password)]
            public string NewPassword { get; set; }

            [DataType(DataType.Password)]
            [Compare("NewPassword", ErrorMessage = "两次输入的密码不一致")]
            public string ConfirmPassword { get; set; }

            // 不需要修改邮箱可以删掉这个字段
            [EmailAddress]
            public string? NewEmail { get; set; }
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            // 根据输入的邮箱查找目标用户
            var user = await _userManager.FindByEmailAsync(Input.Email);
            if (user == null)
            {
                ModelState.AddModelError(string.Empty, "用户不存在");
                return Page();
            }

            // 1. 重置密码逻辑:内部生成重置token,无需发送邮件
            var resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
            var resetPwdResult = await _userManager.ResetPasswordAsync(user, resetToken, Input.NewPassword);
            if (!resetPwdResult.Succeeded)
            {
                foreach (var error in resetPwdResult.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
                return Page();
            }

            // 2. 修改邮箱逻辑(不需要修改邮箱可以删除这部分)
            if (!string.IsNullOrEmpty(Input.NewEmail) && Input.NewEmail != Input.Email)
            {
                var changeEmailToken = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
                var changeEmailResult = await _userManager.ChangeEmailAsync(user, Input.NewEmail, changeEmailToken);
                if (!changeEmailResult.Succeeded)
                {
                    foreach (var error in changeEmailResult.Errors)
                    {
                        ModelState.AddModelError(string.Empty, error.Description);
                    }
                    return Page();
                }
                // 如果你的业务逻辑中用户名和邮箱一致,同步更新用户名
                await _userManager.SetUserNameAsync(user, Input.NewEmail);
            }

            // 重置成功后跳转,按需修改目标路径
            return RedirectToPage("./ResetConfirmation");
        }
    }
}

3. 编写前端页面代码

CustomReset.cshtml示例代码:

@page "/customreset"
@model CustomResetModel
@{
    ViewData["Title"] = "账号重置";
}

<h1>@ViewData["Title"]</h1>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.Email">目标用户原邮箱</label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <!-- 不需要修改邮箱就删掉这个字段 -->
            <div class="form-group">
                <label asp-for="Input.NewEmail">新邮箱(不需要修改可不填)</label>
                <input asp-for="Input.NewEmail" class="form-control" />
                <span asp-validation-for="Input.NewEmail" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.NewPassword">新密码</label>
                <input asp-for="Input.NewPassword" class="form-control" />
                <span asp-validation-for="Input.NewPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword">确认新密码</label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">提交重置</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

可选优化

  • 如果是登录用户修改自己的账号,不需要输入原邮箱,直接通过await _userManager.GetUserAsync(User)获取当前登录用户即可,安全性更高
  • 用户改自己密码时可增加旧密码校验逻辑,调用ChangePasswordAsync方法实现,不需要生成重置token

内容的提问来源于stack exchange,提问作者Krellex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 17:36:00