using Dpz.Core.Service.Mediator.Features.Account.Internal;

namespace Dpz.Core.Service.Mediator.Features.Account.Commands;

/// <summary>
/// 处理管理员重置密码请求
/// </summary>
public class ResetAccountPasswordHandler(
    IRepository<User> userRepository,
    IRepository<UserHistory> userHistoryRepository
) : IRequestHandler<ResetAccountPasswordRequest, ResponseResult>
{
    /// <summary>
    /// 重置密码并刷新 SecurityStamp
    /// </summary>
    public async ValueTask<ResponseResult> Handle(
        ResetAccountPasswordRequest request,
        CancellationToken cancellationToken
    )
    {
        var user = await AccountRepositoryHelper.GetUserAsync(
            userRepository,
            request.Account,
            cancellationToken
        );
        if (user == null)
        {
            return ResponseResult.Fail($"账号【{request.Account}】不存在");
        }

        if (AccountRepositoryHelper.IsSystemAccount(user))
        {
            return ResponseResult.Fail("含有System权限的账号不能操作");
        }

        var newHashedPassword = AccountSecurityHelper.CreatePasswordHash(request.Password);
        if (AccountSecurityHelper.VerifyPassword(request.Password, user.Password))
        {
            return ResponseResult.Fail("新密码不能与旧密码一致");
        }

        var newSecurityStamp = AccountSecurityHelper.GenerateSecurityStamp();
        await AccountRepositoryHelper.SaveUserHistoryAsync(
            userHistoryRepository,
            user.Id,
            [
                AccountRepositoryHelper.Change(nameof(user.Key), user.Key, newSecurityStamp),
                AccountRepositoryHelper.Change(
                    nameof(user.Password),
                    user.Password,
                    newHashedPassword
                ),
            ],
            cancellationToken
        );

        var update = Builders<User>
            .Update.Set(x => x.Password, newHashedPassword)
            .Set(x => x.Key, newSecurityStamp)
            .Set(x => x.LastUpdateTime, DateTime.Now);
        await userRepository.UpdateAsync(x => x.Id == user.Id, update, cancellationToken);

        return ResponseResult.Ok();
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个基于 MediatR 模式的命令处理器(Handler),用于处理管理员重置用户密码的请求。


整体结构

类定义

public class ResetAccountPasswordHandler(
    IRepository<User> userRepository,
    IRepository<UserHistory> userHistoryRepository
) : IRequestHandler<ResetAccountPasswordRequest, ResponseResult>
  • 使用主构造函数(C# 12 特性)注入两个依赖:
    • userRepository:用户数据仓储
    • userHistoryRepository:用户历史记录仓储
  • 实现接口 IRequestHandler,处理 ResetAccountPasswordRequest 请求并返回 ResponseResult

核心逻辑 (Handle 方法)

1️⃣ 验证用户是否存在

var user = await AccountRepositoryHelper.GetUserAsync(
    userRepository, request.Account, cancellationToken
);
if (user == null) {
    return ResponseResult.Fail($"账号【{request.Account}】不存在");
}
  • 通过帮助类查询用户
  • 如果用户不存在,返回失败结果

2️⃣ 防止操作系统账号

if (AccountRepositoryHelper.IsSystemAccount(user)) {
    return ResponseResult.Fail("含有System权限的账号不能操作");
}
  • 检查是否为系统账号(超级管理员等特权账号)
  • 系统账号不允许通过此接口重置密码(安全措施)

3️⃣ 生成新密码哈希并验证

var newHashedPassword = AccountSecurityHelper.CreatePasswordHash(request.Password);
if (AccountSecurityHelper.VerifyPassword(request.Password, user.Password)) {
    return ResponseResult.Fail("新密码不能与旧密码一致");
}
  • 对新密码进行哈希处理
  • 验证新旧密码是否相同(防止无意义的重置)

4️⃣ 生成新的安全戳(SecurityStamp)

var newSecurityStamp = AccountSecurityHelper.GenerateSecurityStamp();
  • 生成新的安全戳,用于使所有现有的登录令牌(Token/Cookie)失效
  • 这是一个重要的安全机制:密码重置后强制用户重新登录

5️⃣ 记录历史变更

await AccountRepositoryHelper.SaveUserHistoryAsync(
    userHistoryRepository, user.Id,
    [
        AccountRepositoryHelper.Change(nameof(user.Key), user.Key, newSecurityStamp),
        AccountRepositoryHelper.Change(nameof(user.Password), user.Password, newHashedPassword),
    ],
    cancellationToken
);
  • 将密码和安全戳的变更记录到历史表
  • 使用集合表达式 [...] (C# 12 特性)创建变更列表
  • 便于审计和追溯

6️⃣ 更新用户数据

var update = Builders<User>
    .Update.Set(x => x.Password, newHashedPassword)
    .Set(x => x.Key, newSecurityStamp)
    .Set(x => x.LastUpdateTime, DateTime.Now);
await userRepository.UpdateAsync(x => x.Id == user.Id, update, cancellationToken);
  • 使用 MongoDB 的 Update Builder 构建更新操作
  • 更新三个字段:
    • Password:新密码哈希
    • Key:新安全戳
    • LastUpdateTime:更新时间

7️⃣ 返回成功

return ResponseResult.Ok();

关键设计特点

特性说明
安全性系统账号保护、新旧密码校验、安全戳刷新
可追溯完整的历史记录保存
原子性先记录历史再更新数据(如有异常可回滚)
现代语法使用 C# 12 主构造函数和集合表达式
异步操作全部使用异步方法(ValueTask

潜在改进建议

  1. 事务支持:历史记录和用户更新应在同一事务中
  2. 密码策略:可添加密码复杂度验证
  3. 通知机制:密码重置后可发送邮件/短信通知
  4. 时间处理:建议使用 DateTime.UtcNow 而非 DateTime.Now
评论加载中...