Browse Source

feat: Implement password reuse check

pull/1498/head
colin 3 months ago
parent
commit
4579fc3a6d
  1. 65
      aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/ProfileAppService.cs
  2. 4
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain.Shared/LINGYUN/Abp/Identity/IdentityErrorCodes.cs
  3. 1
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain.Shared/LINGYUN/Abp/Identity/Localization/en.json
  4. 1
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain.Shared/LINGYUN/Abp/Identity/Localization/zh-Hans.json
  5. 3
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/AbpIdentityDomainModule.cs
  6. 33
      aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/PasswordHistoryPasswordValidator.cs

65
aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/ProfileAppService.cs

@ -0,0 +1,65 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Settings;
using Volo.Abp.Users;
namespace LINGYUN.Abp.Account;
[Dependency(ReplaceServices = true)]
public class ProfileAppService : Volo.Abp.Account.ProfileAppService
{
public ProfileAppService(
IdentityUserManager userManager,
IOptions<IdentityOptions> identityOptions)
: base(userManager, identityOptions)
{
}
public async override Task ChangePasswordAsync(ChangePasswordInput input)
{
await IdentityOptions.SetAsync();
var currentUser = await UserManager.GetByIdAsync(CurrentUser.GetId());
if (currentUser.IsExternal)
{
throw new BusinessException(code: IdentityErrorCodes.ExternalUserPasswordChange);
}
if (currentUser.PasswordHash.IsNullOrWhiteSpace())
{
(await UserManager.AddPasswordAsync(currentUser, input.NewPassword)).CheckErrors();
}
else
{
(await UserManager.ChangePasswordAsync(currentUser, input.CurrentPassword, input.NewPassword)).CheckErrors();
}
if (await SettingProvider.IsTrueAsync(IdentitySettingNames.Password.EnablePreventPasswordReuse))
{
var preventPasswordReuseCount = await SettingProvider.GetAsync(IdentitySettingNames.Password.PreventPasswordReuseCount, 6);
currentUser.AddPasswordHistory(input.NewPassword);
if (currentUser.PasswordHistories.Count > preventPasswordReuseCount)
{
var excessCount = currentUser.PasswordHistories.Count - preventPasswordReuseCount;
var oldestHistories = currentUser.PasswordHistories
.OrderBy(x => x.CreatedAt)
.Take(excessCount)
.ToList();
foreach (var history in oldestHistories)
{
currentUser.PasswordHistories.Remove(history);
}
}
}
}
}

4
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain.Shared/LINGYUN/Abp/Identity/IdentityErrorCodes.cs

@ -34,4 +34,8 @@ public class IdentityErrorCodes
/// 验证器验证无效
/// </summary>
public const string AuthenticatorTokenInValid = "Volo.Abp.Identity:020012";
/// <summary>
/// 密码不能与最近{0}次使用的密码相同
/// </summary>
public const string PasswordInHistoryInValid = "Volo.Abp.Identity:020013";
}

1
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain.Shared/LINGYUN/Abp/Identity/Localization/en.json

@ -53,6 +53,7 @@
"Volo.Abp.Identity:020010": "The email address is bound!",
"Volo.Abp.Identity:020011": "Secondary authentication cannot be enabled when an MFA device is not attached!",
"Volo.Abp.Identity:020012": "Invalid authenticator code!",
"Volo.Abp.Identity:020013": "The password cannot be the same as the previous one.!",
"Volo.Abp.Identity:DuplicatePhoneNumber": "Phone number '{0}' is already taken.",
"DisplayName:Abp.Identity.User.SmsNewUserRegister": "Register sms template",
"Description:Abp.Identity.User.SmsNewUserRegister": "When the user registers, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration",

1
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain.Shared/LINGYUN/Abp/Identity/Localization/zh-Hans.json

@ -53,6 +53,7 @@
"Volo.Abp.Identity:020010": "邮件地址已绑定!",
"Volo.Abp.Identity:020011": "未绑定MFA设备时无法启用二次认证!",
"Volo.Abp.Identity:020012": "无效的验证器代码!",
"Volo.Abp.Identity:020013": "密码不能与之前的密码相同!",
"Volo.Abp.Identity:DuplicatePhoneNumber": "手机号 '{0}' 已存在.",
"DisplayName:Abp.Identity.User.SmsNewUserRegister": "新用户注册模板",
"Description:Abp.Identity.User.SmsNewUserRegister": "新用户通过手机注册账号验证码模板",

3
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/LINGYUN/Abp/Identity/AbpIdentityDomainModule.cs

@ -24,7 +24,8 @@ public class AbpIdentityDomainModule : AbpModule
{
PreConfigure<IdentityBuilder>(builder =>
{
builder.AddUserValidator<PhoneNumberUserValidator>();
builder.AddUserValidator<PhoneNumberUserValidator>()
.AddPasswordValidator<PasswordHistoryPasswordValidator>();
});
}

33
aspnet-core/modules/identity/LINGYUN.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/PasswordHistoryPasswordValidator.cs

@ -0,0 +1,33 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Settings;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace Microsoft.AspNetCore.Identity;
public class PasswordHistoryPasswordValidator : IPasswordValidator<IdentityUser>
{
public async virtual Task<IdentityResult> ValidateAsync(UserManager<IdentityUser> manager, IdentityUser user, string password)
{
var settingProvider = manager.ServiceProvider.GetRequiredService<ISettingProvider>();
if (await settingProvider.IsTrueAsync(IdentitySettingNames.Password.EnablePreventPasswordReuse))
{
var preventPasswordReuseCount = await settingProvider.GetAsync(IdentitySettingNames.Password.PreventPasswordReuseCount, 6);
if (preventPasswordReuseCount > 0 && user.PasswordHistories.Any(x => x.Password == password))
{
var localizer = manager.ServiceProvider.GetRequiredService<IStringLocalizer<IdentityResource>>();
return IdentityResult.Failed(new IdentityError
{
Code = LINGYUN.Abp.Identity.IdentityErrorCodes.PasswordInHistoryInValid,
Description = localizer["Volo.Abp.Identity:PasswordInHistory", preventPasswordReuseCount]
});
}
}
return IdentityResult.Success;
}
}
Loading…
Cancel
Save