ASP.NET Core Identity 框架中实现密码重置

发布于:2025-05-17 ⋅ 阅读:(20) ⋅ 点赞:(0)


前言

密码重置功能

一、密码重置流程原理

  • 核心步骤
    • 用户请求重置密码(提供注册邮箱/用户名)
    • 系统生成密码重置令牌并发送到用户邮箱
    • 用户点击邮件中的链接,进入密码重置页面
    • 提交新密码和令牌,系统验证并更新密码
  • 关键技术
    • UserManager.GeneratePasswordResetTokenAsync() - 生成加密令牌
    • UserManager.ResetPasswordAsync() - 验证令牌并更新密码
    • 邮件服务集成(SMTP 或第三方服务)

二、实现步骤

1)安装 NuGet 包

  1. 安装MailKit
    Install-Package MailKit
    

2)配置邮箱(示例使用 MailKit )

  1. appsettings.json

      "EmailSettings": {
        "SmtpServer": "smtp.163.com",
        "Port": 465, //465
        "UserName": "XXX@163.com",
        "Password": "DJgaznENCZByW4kW", //16位授权码(qq邮箱授权码或网易邮箱授权码,不是邮箱密码)
        "FromAddress": "XXX@163.com"
      }
    
  2. EmailSettings.cs邮箱配置类

    namespace IdentityProject.Entity
    {
        public class EmailSettings
        {
            public string SmtpServer { get; set; }
            public int Port { get; set; }
            public string UserName { get; set; }
            public string Password { get; set; }
            public string FromAddress { get; set; }
        }
    }
    

3)实现邮件发送类

  1. 代码如下(示例):
    using IdentityProject.Entity;
    using Microsoft.AspNetCore.Identity.UI.Services;
    using Microsoft.Extensions.Options;
    using MimeKit;
    using System.Net;
    using System.Net.Http;
    using System.Net.Mail;
    
    namespace IdentityProject.Service
    {
        public class EmailSender : IEmailSender
        {
            private readonly EmailSettings _emailSettings;
    
            public EmailSender(IOptions<EmailSettings> emailSettings)
            {
                _emailSettings = emailSettings.Value;
            }
    
            public async Task SendEmailAsync(string email, string subject, string htmlMessage)
            {
                // 创建邮件对象
                var message = new MimeMessage();
                message.From.Add(new MailboxAddress(
                    "测试邮箱",//邮箱昵称
                    _emailSettings.UserName
                ));
                message.To.Add(MailboxAddress.Parse(email));
                message.Subject = subject;
    
                // 构建HTML正文
                var bodyBuilder = new BodyBuilder
                {
                    HtmlBody = htmlMessage,
                    TextBody = "您的邮件客户端不支持HTML显示"
                };
                message.Body = bodyBuilder.ToMessageBody();
    
                // 配置SMTP客户端
                using var client = new MailKit.Net.Smtp.SmtpClient();
    
                // 强制使用 TLS 1.2
                client.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
    
                await client.ConnectAsync(
               _emailSettings.SmtpServer,
               _emailSettings.Port,
               true
           );
    
                // 认证
                await client.AuthenticateAsync(
                    _emailSettings.UserName,
                    _emailSettings.Password
                );
    
                // 发送邮件
                await client.SendAsync(message);
                await client.DisconnectAsync(true);
            }
        }
    }
    
    

4)注册服务

  1. 代码如下(示例):
    builder.Services.Configure<EmailSettings>(builder.Configuration.GetSection("EmailSettings"));
    builder.Services.AddScoped<IEmailSender, EmailSender>();
    //调整令牌有效期(默认1天)
    builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
    {
        options.TokenLifespan = TimeSpan.FromHours(2); // 设置为2小时
    });
    

5)创建密码重置端点(控制器Controller)

  1. 示例代码AccountController.cs
    using IdentityProject.Entity;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Identity.UI.Services;
    using Microsoft.AspNetCore.Mvc;
    using System.Net;
    
    namespace IdentityProject.Controllers
    {
        [Route("api/[controller]/[action]")]
        [ApiController]
        public class AccountController : ControllerBase
        {
            private readonly UserManager<ApplicationUser> _userManager;
            private readonly IEmailSender _emailSender;
    
            public AccountController(UserManager<ApplicationUser> userManager, IEmailSender emailSender)
            {
                _userManager = userManager;
                _emailSender = emailSender;
            }
            /// <summary>
            /// 请求重置密码
            /// </summary>
            /// <param name="model"></param>
            /// <returns></returns>
            [HttpPost]
            public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordRequest model)
            { 
                var user=await _userManager.FindByEmailAsync(model.Email);
                var user1 = await _userManager.FindByNameAsync("LGF");
                if (user == null) return Ok();
    
                var token=await _userManager.GeneratePasswordResetTokenAsync(user);
                var resetLink= $"https://your@domain.com/reset-password?email={model.Email}&token={WebUtility.UrlEncode(token)}";
    
                await _emailSender.SendEmailAsync(
                    model.Email, 
                    "重置密码", 
                    $"请点击链接重置密码:<a href='{resetLink}'>{resetLink}</a>");
                return Ok();
            }
            //设置新密码
            [HttpPost("reset-password")]
            public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordRequest model)
            {
                ApplicationUser user = await _userManager.FindByEmailAsync(model.Email);
                if (user == null) return BadRequest("用户不存在");
    
                var result = await _userManager.ResetPasswordAsync(user, model.Token, model.NewPassword);
                return result.Succeeded ? Ok() : BadRequest(result.Errors);
            }
        }
    }
    
    namespace IdentityProject.Entity
    {
        public class ForgotPasswordRequest
        {
            public string Email { get; set; }
        }
    }
    
    namespace IdentityProject.Entity
    {
        public class ResetPasswordRequest
        {
            public string Email { get; set; }
            public string Token { get; set; }
            public string NewPassword { get; set; }
        }
    }
    

三、关键注意事项

1)授权码获取

  • 登录163邮箱→POP3/SMTP/IMAP→开启服务(IMAP/SMTP服务)→授权密码管理→新增授权密码

2)端口选择

端口 加密方式 MailKit 参数
465 SSL useSsl: true
587 TLS useSsl: true

3)编码问题

  1. 如果遇到乱码,强制指定编码
    // 如果遇到乱码,强制指定编码
    message.SubjectEncoding = Encoding.UTF8;
    bodyBuilder.HtmlBody = WebUtility.HtmlEncode(htmlMessage);
    

4)异常处理

  1. 参考代码
    catch (AuthenticationException ex)
    {
        Console.WriteLine($"认证失败: {ex.Message}");
    }
    catch (SmtpCommandException ex)
    {
        Console.WriteLine($"SMTP错误 (状态码: {ex.StatusCode}): {ex.Message}");
    }
    catch (IOException ex)
    {
        Console.WriteLine($"网络错误: {ex.Message}");
    }
    

四、常见问题排查

  1. 问题 1:超时无法连接
    增加超时设置
    client.Timeout = 30000; // 30秒
    
  2. 问题 2:证书验证失败
    跳过证书验证(仅测试环境使用)
    client.ServerCertificateValidationCallback = (s, c, h, e) => true;
    
  3. 邮件被识别为垃圾邮件
    在邮件头中添加 X-Mailer 标识
    message.Headers.Add("X-Mailer", "MyApp Mail System");
    

总结

通过以上步骤,可实现密码重置功能


网站公告

今日签到

点亮在社区的每一天
去签到