diff --git a/Source/Starfish.Common/Constants.cs b/Source/Starfish.Common/Constants.cs index 4d7fecf..41cdbc6 100644 --- a/Source/Starfish.Common/Constants.cs +++ b/Source/Starfish.Common/Constants.cs @@ -37,5 +37,6 @@ public static class Configuration public static class RegexPattern { public const string Secret = @"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,32}$"; + public const string Password = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\x00-\xff]{8,32}$"; } } \ No newline at end of file diff --git a/Source/Starfish.Service/Application/Commands/Identity/ChangePasswordCommand.cs b/Source/Starfish.Service/Application/Commands/Identity/ChangePasswordCommand.cs index d21d04d..2333a0e 100644 --- a/Source/Starfish.Service/Application/Commands/Identity/ChangePasswordCommand.cs +++ b/Source/Starfish.Service/Application/Commands/Identity/ChangePasswordCommand.cs @@ -11,11 +11,12 @@ public ChangePasswordCommand() { } - public ChangePasswordCommand(string userId, string password) + public ChangePasswordCommand(string userId, string password, string actionType) : this() { UserId = userId; Password = password; + ActionType = actionType; } /// @@ -27,4 +28,10 @@ public ChangePasswordCommand(string userId, string password) /// 新密码 /// public string Password { get; set; } + + /// + /// 操作方式 + /// + /// change-修改;reset-重置 + public string ActionType { get; set; } } \ No newline at end of file diff --git a/Source/Starfish.Service/Application/Handlers/UserCommandHandler.cs b/Source/Starfish.Service/Application/Handlers/UserCommandHandler.cs index 9a35d2d..c28aceb 100644 --- a/Source/Starfish.Service/Application/Handlers/UserCommandHandler.cs +++ b/Source/Starfish.Service/Application/Handlers/UserCommandHandler.cs @@ -10,10 +10,10 @@ namespace Nerosoft.Starfish.Application; /// 用户命令处理器 /// public sealed class UserCommandHandler : CommandHandlerBase, - IHandler, - IHandler, - IHandler, - IHandler + IHandler, + IHandler, + IHandler, + IHandler { /// /// 初始化. @@ -58,7 +58,7 @@ public Task HandleAsync(UserUpdateCommand message, MessageContext context, Cance business.IsAdmin = message.Item2.IsAdmin; business.MarkAsUpdate(); - + await business.SaveAsync(true, cancellationToken); }); } @@ -68,11 +68,7 @@ public Task HandleAsync(ChangePasswordCommand message, MessageContext context, C { return ExecuteAsync(async () => { - var business = await Factory.FetchAsync(message.UserId, cancellationToken); - - business.Password = message.Password; - business.MarkAsUpdate(); - await business.SaveAsync(true, cancellationToken); + _ = await Factory.ExecuteAsync(message.UserId, message.Password, message.ActionType, cancellationToken); }); } diff --git a/Source/Starfish.Service/Domain/Aggregates/User.cs b/Source/Starfish.Service/Domain/Aggregates/User.cs index 04141ab..4fb6b9f 100644 --- a/Source/Starfish.Service/Domain/Aggregates/User.cs +++ b/Source/Starfish.Service/Domain/Aggregates/User.cs @@ -21,14 +21,13 @@ private User() /// 初始化用户聚合根 /// /// - /// - /// - private User(string userName, string passwordHash, string passwordSalt) + /// + /// + private User(string userName, string password) : this() { UserName = userName; - PasswordHash = passwordHash; - PasswordSalt = passwordSalt; + SetPassword(password); } /// @@ -115,22 +114,25 @@ private User(string userName, string passwordHash, string passwordSalt) /// internal static User Create(string userName, string password) { - var salt = RandomUtility.GenerateUniqueId(); - var hash = Cryptography.DES.Encrypt(password, Encoding.UTF8.GetBytes(salt)); - var entity = new User(userName, hash, salt); + var entity = new User(userName, password); return entity; } /// - /// 修改密码 + /// 设置密码 /// /// - internal void ChangePassword(string password) + /// + internal void SetPassword(string password, string actionType = null) { var salt = RandomUtility.GenerateUniqueId(); var hash = Cryptography.DES.Encrypt(password, Encoding.UTF8.GetBytes(salt)); PasswordHash = hash; PasswordSalt = salt; + if (!string.IsNullOrWhiteSpace(actionType)) + { + RaiseEvent(new UserPasswordChangedEvent { Type = actionType }); + } } /// diff --git a/Source/Starfish.Service/Domain/Business/UserGeneralBusiness.cs b/Source/Starfish.Service/Domain/Business/UserGeneralBusiness.cs index 44ed584..00e2944 100644 --- a/Source/Starfish.Service/Domain/Business/UserGeneralBusiness.cs +++ b/Source/Starfish.Service/Domain/Business/UserGeneralBusiness.cs @@ -160,11 +160,6 @@ protected override Task UpdateAsync(CancellationToken cancellationToken = defaul Aggregate.SetNickName(NickName); } - if (ChangedProperties.Contains(PasswordProperty)) - { - Aggregate.ChangePassword(Password); - } - if (ChangedProperties.Contains(IsAdminProperty)) { Aggregate.SetIsAdmin(IsAdmin); @@ -272,8 +267,6 @@ public override async Task ExecuteAsync(IRuleContext context, CancellationToken public class PasswordStrengthRule : RuleBase { - private const string REGEX_PATTERN = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\x00-\xff]{8,32}$"; - public override Task ExecuteAsync(IRuleContext context, CancellationToken cancellationToken = default) { var target = (UserGeneralBusiness)context.Target; @@ -284,12 +277,12 @@ public override Task ExecuteAsync(IRuleContext context, CancellationToken cancel { context.AddErrorResult(Resources.IDS_ERROR_USER_RULE_PASSWORD_REQUIRED); } - else if (!Regex.IsMatch(target.Password, REGEX_PATTERN)) + else if (!Regex.IsMatch(target.Password, Constants.RegexPattern.Password)) { context.AddErrorResult(Resources.IDS_ERROR_USER_RULE_PASSWORD_NOT_MARCHED_RULES); } } - else if (target.IsUpdate && target.ChangedProperties.Contains(PasswordProperty) && !Regex.IsMatch(target.Password, REGEX_PATTERN)) + else if (target.IsUpdate && target.ChangedProperties.Contains(PasswordProperty) && !Regex.IsMatch(target.Password, Constants.RegexPattern.Password)) { context.AddErrorResult(Resources.IDS_ERROR_USER_RULE_PASSWORD_NOT_MARCHED_RULES); } diff --git a/Source/Starfish.Service/Domain/Business/UserPasswordBusiness.cs b/Source/Starfish.Service/Domain/Business/UserPasswordBusiness.cs new file mode 100644 index 0000000..b21ebf4 --- /dev/null +++ b/Source/Starfish.Service/Domain/Business/UserPasswordBusiness.cs @@ -0,0 +1,36 @@ +using System.Text.RegularExpressions; +using Nerosoft.Euonia.Business; +using Nerosoft.Euonia.Domain; +using Nerosoft.Starfish.Service; + +namespace Nerosoft.Starfish.Domain; + +internal class UserPasswordBusiness : CommandObjectBase, IDomainService +{ + [Inject] + public IUserRepository Repository { get; set; } + + [FactoryExecute] + protected async Task ExecuteAsync(string id, string password, string actionType, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(password)) + { + throw new ValidationException(Resources.IDS_ERROR_USER_RULE_PASSWORD_REQUIRED); + } + else if (!Regex.IsMatch(password, Constants.RegexPattern.Password)) + { + throw new ValidationException(Resources.IDS_ERROR_USER_RULE_PASSWORD_NOT_MARCHED_RULES); + } + + var aggregate = await Repository.GetAsync(id, true, cancellationToken); + + if (aggregate == null) + { + throw new UserNotFoundException(id); + } + + aggregate.SetPassword(password, actionType); + + await Repository.UpdateAsync(aggregate, true, cancellationToken); + } +} diff --git a/Source/Starfish.Service/Domain/Events/UserPasswordChangedEvent.cs b/Source/Starfish.Service/Domain/Events/UserPasswordChangedEvent.cs index e18874f..e788d69 100644 --- a/Source/Starfish.Service/Domain/Events/UserPasswordChangedEvent.cs +++ b/Source/Starfish.Service/Domain/Events/UserPasswordChangedEvent.cs @@ -7,4 +7,12 @@ namespace Nerosoft.Starfish.Domain; /// public sealed class UserPasswordChangedEvent : DomainEvent { + /// + /// 操作类型 + /// + /// + /// - change + /// - reset + /// + public string Type { get; set; } } \ No newline at end of file diff --git a/Source/Starfish.Service/UseCases/Identity/ChangePasswordUseCase.cs b/Source/Starfish.Service/UseCases/Identity/ChangePasswordUseCase.cs index 8bd518b..11a7215 100644 --- a/Source/Starfish.Service/UseCases/Identity/ChangePasswordUseCase.cs +++ b/Source/Starfish.Service/UseCases/Identity/ChangePasswordUseCase.cs @@ -45,7 +45,7 @@ public async Task ExecuteAsync(ChangePasswordInput input, CancellationToken canc throw new BadRequestException(Resources.IDS_ERROR_PASSWORD_INCORRECT); } - var command = new ChangePasswordCommand(user.Id, input.NewPassword); + var command = new ChangePasswordCommand(user.Id, input.NewPassword, "change"); await _bus.SendAsync(command, cancellationToken); } } \ No newline at end of file diff --git a/Source/Starfish.Service/UseCases/Identity/ResetPasswordUseCase.cs b/Source/Starfish.Service/UseCases/Identity/ResetPasswordUseCase.cs index 1240a2f..77ab0db 100644 --- a/Source/Starfish.Service/UseCases/Identity/ResetPasswordUseCase.cs +++ b/Source/Starfish.Service/UseCases/Identity/ResetPasswordUseCase.cs @@ -19,7 +19,7 @@ public ResetPasswordUseCase(IBus bus) public Task ExecuteAsync(ResetPasswordInput input, CancellationToken cancellationToken = default) { - var command = new ChangePasswordCommand(input.Id, input.Password); + var command = new ChangePasswordCommand(input.Id, input.Password, "reset"); return _bus.SendAsync(command, cancellationToken); } } \ No newline at end of file diff --git a/Source/Starfish.Webapi/Controllers/UserController.cs b/Source/Starfish.Webapi/Controllers/UserController.cs index b43c90e..8b8810e 100644 --- a/Source/Starfish.Webapi/Controllers/UserController.cs +++ b/Source/Starfish.Webapi/Controllers/UserController.cs @@ -102,7 +102,7 @@ public async Task DeleteAsync(string id) await _service.DeleteAsync(id, HttpContext.RequestAborted); return Ok(); } - + /// /// 重置指定用户密码 ///