پیاده سازی Password Policy سفارشی توسط ASP.NET Identity
نویسنده: آرمین ضیاء
تاریخ: ۱۳۹۲/۱۰/۲۲ ۲۱:۱۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
در پنجره Solution Explorer روی نام پروژه کلیک راست کنید و گزینه Manage NuGet Packages را انتخاب کنید. به قسمت Update بروید و تمام انتشارات جدید را در صورت وجود نصب کنید.
بگذارید تا به روند کلی ایجاد کاربران جدید در اپلیکیشن نگاهی بیاندازیم. این به ما در شناسایی نیازهای جدیدمان کمک میکند. در پوشه Controllers فایلی بنام AccountController.cs وجود دارد که حاوی متدهایی برای مدیریت کاربران است.
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
} public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(): base(new UserStore<ApplicationUser>(new ApplicationDbContext()))
{
PasswordValidator = new MinimumLengthValidator (10);
}
} public AccountController() : this(new ApplicationUserManager())
{
}
public AccountController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
public ApplicationUserManager UserManager { get; private set; }
public class CustomPasswordValidator : IIdentityValidator<string>
{
public int RequiredLength { get; set; }
public CustomPasswordValidator(int length)
{
RequiredLength = length;
}
public Task<IdentityResult> ValidateAsync(string item)
{
if (String.IsNullOrEmpty(item) || item.Length < RequiredLength)
{
return Task.FromResult(IdentityResult.Failed(String.Format("Password should be of length {0}",RequiredLength)));
}
string pattern = @"^(?=.*[0-9])(?=.*[!@#$%^&*])[0-9a-zA-Z!@#$%^&*0-9]{10,}$";
if (!Regex.IsMatch(item, pattern))
{
return Task.FromResult(IdentityResult.Failed("Password should have one numeral and one special character"));
}
return Task.FromResult(IdentityResult.Success);
} public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager() : base(new UserStore<ApplicationUser(new ApplicationDbContext()))
{
PasswordValidator = new CustomPasswordValidator(10);
}
}
public class PreviousPassword
{
public PreviousPassword()
{
CreateDate = DateTimeOffset.Now;
}
[Key, Column(Order = 0)]
public string PasswordHash { get; set; }
public DateTimeOffset CreateDate { get; set; }
[Key, Column(Order = 1)]
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
} public class ApplicationUser : IdentityUser
{
public ApplicationUser() : base()
{
PreviousUserPasswords = new List<PreviousPassword>();
}
public virtual IList<PreviousPassword> PreviousUserPasswords { get; set; }
} public class ApplicationUserStore : UserStore<ApplicationUser>
{
public ApplicationUserStore(DbContext context) : base(context) { }
public override async Task CreateAsync(ApplicationUser user)
{
await base.CreateAsync(user);
await AddToPreviousPasswordsAsync(user, user.PasswordHash);
}
public Task AddToPreviousPasswordsAsync(ApplicationUser user, string password)
{
user.PreviousUserPasswords.Add(new PreviousPassword() { UserId = user.Id, PasswordHash = password });
return UpdateAsync(user);
}
} public class ApplicationUserManager : UserManager<ApplicationUser>
{
private const int PASSWORD_HISTORY_LIMIT = 5;
public ApplicationUserManager() : base(new ApplicationUserStore(new ApplicationDbContext()))
{
PasswordValidator = new CustomPasswordValidator(10);
}
public override async Task<IdentityResult> ChangePasswordAsync(string userId, string currentPassword, string newPassword)
{
if (await IsPreviousPassword(userId, newPassword))
{
return await Task.FromResult(IdentityResult.Failed("Cannot reuse old password"));
}
var result = await base.ChangePasswordAsync(userId, currentPassword, newPassword);
if (result.Succeeded)
{
var store = Store as ApplicationUserStore;
await store.AddToPreviousPasswordsAsync(await FindByIdAsync(userId), PasswordHasher.HashPassword(newPassword));
}
return result;
}
public override async Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword)
{
if (await IsPreviousPassword(userId, newPassword))
{
return await Task.FromResult(IdentityResult.Failed("Cannot reuse old password"));
}
var result = await base.ResetPasswordAsync(userId, token, newPassword);
if (result.Succeeded)
{
var store = Store as ApplicationUserStore;
await store.AddToPreviousPasswordsAsync(await FindByIdAsync(userId), PasswordHasher.HashPassword(newPassword));
}
return result;
}
private async Task<bool> IsPreviousPassword(string userId, string newPassword)
{
var user = await FindByIdAsync(userId);
if (user.PreviousUserPasswords.OrderByDescending(x => x.CreateDate).
Select(x => x.PasswordHash).Take(PASSWORD_HISTORY_LIMIT)
.Where(x => PasswordHasher.VerifyHashedPassword(x, newPassword) != PasswordVerificationResult.Failed).Any())
{
return true;
}
return false;
}
}
سورس کد این مثال را میتوانید از این لینک دریافت کنید. نام پروژه Identity-PasswordPolicy است، و زیر قسمت Samples/Identity قرار دارد.