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) |
潜在改进建议
- 事务支持:历史记录和用户更新应在同一事务中
- 密码策略:可添加密码复杂度验证
- 通知机制:密码重置后可发送邮件/短信通知
- 时间处理:建议使用
DateTime.UtcNow而非DateTime.Now
AI 正在分析代码…
评论加载中...