using System.ComponentModel.DataAnnotations;

namespace Dpz.Core.Auth.Models;

public class ChangePasswordModel : IValidatableObject
{
    /// <summary>
    /// 当前密码
    /// </summary>
    [Required(ErrorMessage = "请输入当前密码")]
    [Display(Name = "当前密码")]
    public string? Password { get; set; }

    /// <summary>
    /// 新密码
    /// </summary>
    [Required(ErrorMessage = "请输入新密码")]
    [StringLength(100, MinimumLength = 8, ErrorMessage = "新密码长度必须在8-100个字符之间")]
    [RegularExpression(
        @"^[\x21-\x7E]{8,}$",
        ErrorMessage = "新密码只能包含ASCII可打印字符,不能包含空格"
    )]
    [Display(Name = "新密码")]
    public string? NewPassword { get; set; }

    /// <summary>
    /// 新密码确认
    /// </summary>
    [Required(ErrorMessage = "请确认新密码")]
    [Display(Name = "确认新密码")]
    public string? NewRepeatPassword { get; set; }

    /// <summary>
    /// 双因素验证码(如已启用)
    /// </summary>
    [StringLength(6, MinimumLength = 6, ErrorMessage = "验证码必须为6位数字")]
    [RegularExpression(@"^\d{6}$", ErrorMessage = "验证码必须为6位数字")]
    [Display(Name = "双因素验证码")]
    public string? PinCode { get; set; }

    /// <summary>
    /// 自定义验证逻辑
    /// </summary>
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // 验证新密码确认
        if (!string.IsNullOrEmpty(NewPassword) && !string.IsNullOrEmpty(NewRepeatPassword))
        {
            if (NewPassword != NewRepeatPassword)
            {
                yield return new ValidationResult(
                    "新密码与确认密码不匹配",
                    new[] { nameof(NewRepeatPassword) }
                );
            }
        }

        // 验证新密码不能与当前密码相同
        if (!string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(NewPassword))
        {
            if (Password == NewPassword)
            {
                yield return new ValidationResult(
                    "新密码不能与当前密码相同",
                    new[] { nameof(NewPassword) }
                );
            }
        }
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个用于用户修改密码功能的数据模型类,实现了 IValidatableObject 接口来提供自定义验证逻辑。让我详细解释一下代码的各个部分:

类概述

public class ChangePasswordModel : IValidatableObject

这个类继承了 IValidatableObject 接口,允许实现自定义的验证逻辑。

属性详解

1. 当前密码 (Password)

[Required(ErrorMessage = "请输入当前密码")]
[Display(Name = "当前密码")]
public string? Password { get; set; }
  • 使用 Required 特性标记为必填字段
  • 用于验证用户身份的当前密码

2. 新密码 (NewPassword)

[Required(ErrorMessage = "请输入新密码")]
[StringLength(100, MinimumLength = 8, ErrorMessage = "新密码长度必须在8-100个字符之间")]
[RegularExpression(@"^[\x21-\x7E]{8,}$", ErrorMessage = "新密码只能包含ASCII可打印字符,不能包含空格")]
[Display(Name = "新密码")]
public string? NewPassword { get; set; }
  • 必填验证:确保用户输入新密码
  • 长度验证:密码长度必须在8-100个字符之间
  • 格式验证:正则表达式 [\x21-\x7E]{8,} 确保密码只包含ASCII可打印字符(不包含空格),提高安全性

3. 新密码确认 (NewRepeatPassword)

[Required(ErrorMessage = "请确认新密码")]
[Display(Name = "确认新密码")]
public string? NewRepeatPassword { get; set; }
  • 用于确保用户正确输入了新密码

4. 双因素验证码 (PinCode)

[StringLength(6, MinimumLength = 6, ErrorMessage = "验证码必须为6位数字")]
[RegularExpression(@"^\d{6}$", ErrorMessage = "验证码必须为6位数字")]
[Display(Name = "双因素验证码")]
public string? PinCode { get; set; }
  • 可选字段(没有 Required 特性)
  • 如果启用了双因素认证,需要输入6位数字验证码
  • 正则表达式 ^\d{6}$ 确保输入的是恰好6位数字

自定义验证逻辑

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

这个方法实现了两个重要的业务验证规则:

1. 新密码一致性验证

if (NewPassword != NewRepeatPassword)
{
    yield return new ValidationResult(
        "新密码与确认密码不匹配",
        new[] { nameof(NewRepeatPassword) }
    );
}

确保用户在两个密码字段中输入了相同的密码。

2. 密码差异性验证

if (Password == NewPassword)
{
    yield return new ValidationResult(
        "新密码不能与当前密码相同",
        new[] { nameof(NewPassword) }
    );
}

防止用户将新密码设置为与当前密码相同的值。

设计优点

  1. 多层验证:结合了特性验证和自定义验证逻辑
  2. 安全性考虑:密码复杂度要求、长度限制
  3. 用户体验:清晰的错误提示信息
  4. 扩展性:支持双因素认证
  5. 可维护性:代码结构清晰,易于理解和修改

这个模型类非常适合用在Web应用程序的密码修改功能中,提供了完整的验证机制来确保密码修改过程的安全性和用户体验。

评论加载中...