После обновления таблиц идентификации вы можете обновить существующих пользователей хэши паролей.Некоторые новые столбцы в таблице AspNetUsers
будут иметь значения NULL
.Сначала запустите это:
UPDATE AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(Email)
WHERE NormalizedEmail IS NULL
NormalizedUserName фактически обновлено в верхнем регистре email .
Нам нужен способ, чтобы отличить пользователейиспользовать новую версию хэша или нет.
Один из способов - добавить новое свойство в IdentityUser:
public class ApplicationUser : IdentityUser
{
public PasswordHashVersion HashVersion { get; set; }
public ApplicationUser()
{
this.HashVersion = PasswordHashVersion.Core;
}
}
public enum PasswordHashVersion
{
OldMvc,
Core
}
Для существующих пользователей будет задано значение по умолчанию PasswordHashVersion
равно нулю (OldMvc), новые зарегистрированныепользователи будут по умолчанию один (основной).Если у вас есть более умный способ определить, является ли хэш новым или старым алгоритмом, вам это не нужно.
Затем мы создадим пользовательский PasswordHash, который использует реализацию старого алгоритма хеширования по умолчанию :
public class OldMvcPasswordHasher : PasswordHasher<ApplicationUser>
{
public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
{
// if it's the new algorithm version, delegate the call to parent class
if (user.HashVersion == PasswordHashVersion.Core)
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
byte[] buffer4;
if (hashedPassword == null)
{
return PasswordVerificationResult.Failed;
}
if (providedPassword == null)
{
throw new ArgumentNullException("providedPassword");
}
byte[] src = Convert.FromBase64String(hashedPassword);
if ((src.Length != 0x31) || (src[0] != 0))
{
return PasswordVerificationResult.Failed;
}
byte[] dst = new byte[0x10];
Buffer.BlockCopy(src, 1, dst, 0, 0x10);
byte[] buffer3 = new byte[0x20];
Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(providedPassword, dst, 0x3e8))
{
buffer4 = bytes.GetBytes(0x20);
}
if (AreHashesEqual(buffer3, buffer4))
{
user.HashVersion = PasswordHashVersion.Core;
return PasswordVerificationResult.SuccessRehashNeeded;
}
return PasswordVerificationResult.Failed;
}
private bool AreHashesEqual(byte[] firstHash, byte[] secondHash)
{
int _minHashLength = firstHash.Length <= secondHash.Length ? firstHash.Length : secondHash.Length;
var xor = firstHash.Length ^ secondHash.Length;
for (int i = 0; i < _minHashLength; i++)
xor |= firstHash[i] ^ secondHash[i];
return 0 == xor;
}
}
Этот класс наследует новое Identity Core PasswordHasher
.Если версия хэша пароля пользователя уже использует новый алгоритм (например, HashVersion = Core), то мы просто вызываем базовый метод из PasswordHasher
, который использует новый алгоритм.В противном случае используйте старый алгоритм идентификации для проверки пароля.
Если пароль совпадает, мы обновляем версию хэша пароля пользователя до Core
и возвращаем PasswordVerificationResult.SuccessRehashNeeded
, чтобы принудительно обновить существующий хэш с помощью нового алгоритма..
Наконец, вам нужно убедиться, что ваш пользовательский PasswordHasher
используется.Добавьте это к Startup.cs
внутри ConfigureServices
:
// Replace the existing scoped IPasswordHasher<> implementation
services.Replace(new ServiceDescriptor(
serviceType: typeof(IPasswordHasher<ApplicationUser>),
implementationType: typeof(OldMvcPasswordHasher),
ServiceLifetime.Scoped));
Это необходимо добавить после любых вызовов на AddIdentity
, AddDefaultIdentity
или AddIdentityCore
.
Это будет медленно обновлять хэши паролей по мере аутентификации ваших пользователей.