diff --git a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs index 96c9037d2..5f65c4d00 100644 --- a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs +++ b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs @@ -39,8 +39,11 @@ namespace IRaCIS.Application.Contracts public UserTypeEnum UserTypeEnum { get; set; } - - public bool IsTestUser { get; set; } + /// + /// 上一次修改密码的时间 + /// + public DateTime? LastChangePassWordTime { get; set; } + public bool IsTestUser { get; set; } public bool IsAdmin { get; set; } = false; public string UserTypeShortName { get; set; } = string.Empty; public bool PasswordChanged { get; set; } @@ -55,7 +58,9 @@ namespace IRaCIS.Application.Contracts public bool IsFirstAdd { get; set; } public bool IsReviewer { get; set; } = false; - } + public int LoginState { get; set; } = 0; + + } public class MenuFuncTreeNodeView { diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs index 945fd09ec..27f6734ea 100644 --- a/IRaCIS.Core.Application/Service/Management/UserService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -23,13 +23,14 @@ namespace IRaCIS.Application.Services public class UserService : BaseService, IUserService { private readonly IRepository _userRepository; - private readonly IMailVerificationService _mailVerificationService; + + private readonly IMailVerificationService _mailVerificationService; private readonly IRepository _verificationCodeRepository; private readonly IRepository _doctorRepository; private readonly IRepository _userTrialRepository; private readonly IRepository _userLogRepository; - - private readonly IDistributedLockProvider _distributedLockProvider; + private readonly IRepository _userPassWordLogRepository; + private readonly IDistributedLockProvider _distributedLockProvider; private readonly IEasyCachingProvider _cache; private readonly IReadingImageTaskService _readingImageTaskService; private readonly IOptionsMonitor _verifyConfig; @@ -44,13 +45,14 @@ namespace IRaCIS.Application.Services IReadingImageTaskService readingImageTaskService, IRepository userTrialRepository, IOptionsMonitor verifyConfig, - IRepository userLogRepository + IRepository userLogRepository, + IRepository userPassWordLogRepository , IDistributedLockProvider distributedLockProvider) { _userLogRepository = userLogRepository; - - _verifyConfig = verifyConfig; + this._userPassWordLogRepository = userPassWordLogRepository; + _verifyConfig = verifyConfig; _cache = cache; this._readingImageTaskService = readingImageTaskService; _userRepository = userRepository; @@ -94,36 +96,51 @@ namespace IRaCIS.Application.Services private async Task VerifyUserPwdAsync(Guid userId, string newPwd, string? oldPwd = null) { - //var dbUser = (await _userRepository.FirstOrDefaultAsync(t => t.Id == userId)).IfNullThrowException(); + //var dbUser = (await _userRepository.FirstOrDefaultAsync(t => t.Id == userId)).IfNullThrowException(); - if (_verifyConfig.CurrentValue.OpenUserComplexPassword) + if (oldPwd != null && oldPwd == newPwd) + { + //---新密码与旧密码相同。 + throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); + } + + + var dbUser = (await _userRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); + + if (oldPwd != null && dbUser.Password != oldPwd) + { + //---旧密码验证失败。 + throw new BusinessValidationFailedException(_localizer["User_OldPwdInvalid"]); + } + + if (dbUser.Password == newPwd) + { + //---新密码与旧密码相同。 + throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); + } + + var passWordList = await _userPassWordLogRepository.Where(x => x.UserId == userId).OrderByDescending(x => x.CreateTime).Take(2).ToListAsync(); + if (passWordList.Any(x => x.PassWord == newPwd)) + { + throw new BusinessValidationFailedException(_localizer["User_PassWordRepeat"]); + } + + await _userPassWordLogRepository.AddAsync(new UserPassWordLog() { - if (oldPwd != null && oldPwd == newPwd) - { - //---新密码与旧密码相同。 - throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); - } + CreateTime = DateTime.Now, + PassWord = oldPwd, + UserId = userId, + }); + + await _userRepository.BatchUpdateNoTrackingAsync(x => x.Id == userId, x => new User() + { + LastChangePassWordTime = DateTime.Now, + }); + await _userPassWordLogRepository.SaveChangesAsync(); - var dbUser = (await _userRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); - - if (oldPwd != null && dbUser.Password != oldPwd) - { - //---旧密码验证失败。 - throw new BusinessValidationFailedException(_localizer["User_OldPwdInvalid"]); - } - - if (dbUser.Password == newPwd) - { - //---新密码与旧密码相同。 - throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); - } - - - } - - await Task.CompletedTask; + await Task.CompletedTask; } @@ -650,7 +667,7 @@ namespace IRaCIS.Application.Services if (failCount >= maxFailures) { - await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = Guid.Empty, OptUserId = Guid.Empty, LoginFaildName = userName,LoginPassword=password, OptType = UserOptType.AccountLocked }, true); + await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = Guid.Empty, OptUserId = Guid.Empty, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountLocked }, true); //$"密码连续错误{maxFailures}次,当前账号已被限制登录,请等待 {lockoutMinutes} 分钟后再试。" throw new BusinessValidationFailedException(_localizer["User_ErrorLimit", maxFailures, lockoutMinutes]); @@ -668,7 +685,7 @@ namespace IRaCIS.Application.Services failCount++; _cache.Set(cacheKey, failCount, TimeSpan.FromMinutes(lockoutMinutes)); - await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = Guid.Empty, OptUserId=Guid.Empty, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountOrPasswordError }, true); + await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = Guid.Empty, OptUserId = Guid.Empty, LoginFaildName = userName, LoginPassword = password, OptType = UserOptType.AccountOrPasswordError }, true); return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new LoginReturnDTO()); @@ -684,14 +701,44 @@ namespace IRaCIS.Application.Services return ResponseOutput.NotOk(_localizer["User_Disabled"], new LoginReturnDTO()); } + //超过90天没修改密码 + if (loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-90) > loginUser.LastChangePassWordTime.Value) + { + loginUser.LoginState = 1; + } + + //登录成功 清除缓存 _cache.Set(cacheKey, 0, TimeSpan.FromMinutes(lockoutMinutes)); + var lastLoginLog = await _userLogRepository.Where(x => x.LoginUserId == loginUser.Id).OrderByDescending(x => x.CreateTime).FirstOrDefaultAsync(); + if (lastLoginLog!=null) + { + if (lastLoginLog.IP != _userInfo.IP) + { + loginUser.LoginState = 2; + } + + + } + await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = loginUser.Id, OptUserId = loginUser.Id, OptType = UserOptType.Login }, true); userLoginReturnModel.BasicInfo = loginUser; + if (loginUser.LastChangePassWordTime == null) + { + await _userRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.Id, x => new User() + { + LastChangePassWordTime = DateTime.Now + + }); + + } + + + // 登录 清除缓存 //_cache.Remove(userLoginReturnModel.BasicInfo.Id.ToString()); diff --git a/IRaCIS.Core.Domain/Management/User.cs b/IRaCIS.Core.Domain/Management/User.cs index 32ae20058..e47cbd423 100644 --- a/IRaCIS.Core.Domain/Management/User.cs +++ b/IRaCIS.Core.Domain/Management/User.cs @@ -78,8 +78,13 @@ namespace IRaCIS.Core.Domain.Models public string EmailToken { get; set; } = string.Empty; - //ҽ˺ź󣬻ֵ - public Guid? DoctorId { get; set; } + /// + /// һ޸ʱ + /// + public DateTime? LastChangePassWordTime { get; set; } + + //ҽ˺ź󣬻ֵ + public Guid? DoctorId { get; set; } [JsonIgnore] [ForeignKey("DoctorId")] diff --git a/IRaCIS.Core.Domain/Management/UserPassWordLog.cs b/IRaCIS.Core.Domain/Management/UserPassWordLog.cs new file mode 100644 index 000000000..63ceb7d7b --- /dev/null +++ b/IRaCIS.Core.Domain/Management/UserPassWordLog.cs @@ -0,0 +1,43 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-05-07 13:47:08 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///UserPassWordLog + /// + [Table("UserPassWordLog")] + public class UserPassWordLog : Entity, IAuditAdd + { + + + /// + /// 用户Id + /// + public Guid UserId { get; set; } + + /// + /// 密码 + /// + public string PassWord { get; set; } + + /// + /// 创建日期 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs index efd038e73..54522cbbb 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -325,7 +325,9 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet User { get; set; } - public virtual DbSet TrialSiteUserSurvey { get; set; } + public virtual DbSet UserPassWordLog { get; set; } + + public virtual DbSet TrialSiteUserSurvey { get; set; } public virtual DbSet TrialSiteEquipmentSurvey { get; set; } public virtual DbSet TrialSiteSurvey { get; set; } diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs index 58eb41fd6..459ea65fb 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs @@ -33,13 +33,23 @@ namespace IRaCIS.Core.Infrastructure.Extention CloseCurrentWindows = 6, + /// + /// IP不一致 + /// + IpDiscrepancy = 7, - //在其他地方登陆,被迫下线 - LoginInOtherPlace = -1, + + //在其他地方登陆,被迫下线 + LoginInOtherPlace = -1, AutoLoginOut = -2, + /// + /// 需要修改密码 + /// + NeedChangePassWord=-3, + //没有带token访问(未登陆) NoToken = 10, diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs index 0b6e8ea93..af255eeb2 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs @@ -128,9 +128,9 @@ namespace IRaCIS.Core.Infrastructure.Extention // return new ResponseOutput().Ok(data, msg); //} - public static IResponseOutput Ok(T data = default, object otherData = default, string msg = "") + public static IResponseOutput Ok(T data = default, object otherData = default, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) { - return new ResponseOutput().Ok(data, otherData, msg); + return new ResponseOutput().Ok(data, otherData, msg, code); } /// /// 成功 diff --git a/IRaCIS.Core.Test/DbHelper.ttinclude b/IRaCIS.Core.Test/DbHelper.ttinclude index f492fe319..b9c33173c 100644 --- a/IRaCIS.Core.Test/DbHelper.ttinclude +++ b/IRaCIS.Core.Test/DbHelper.ttinclude @@ -1,10 +1,10 @@ <#+ public class config { - public static readonly string ConnectionString = "Server=123.56.94.154,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true"; + public static readonly string ConnectionString = "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true"; public static readonly string DbDatabase = "Test_IRC"; //表名称用字符串,拼接 - public static readonly string TableName = "EnrollReadingCriterion"; + public static readonly string TableName = "UserPassWordLog"; //具体文件里面 例如service 可以配置是否分页 } #>