更新时间:2022-06-24 01:24:30
升级身份表后,您可能需要更新现有用户密码哈希. AspNetUsers
表中的某些新列将具有NULL
值.首先运行:
After upgrading the Identity tables, you may want to update existing users password hashes. Some new columns in the AspNetUsers
table will have NULL
values. First run this:
UPDATE AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(UserName)
WHERE NormalizedEmail IS NULL
我们需要一种方法来区分哪些用户正在使用新的哈希版本.
We need a way to differentiate what users are using the new hash version or not.
一种方法是向IdentityUser添加新属性:
One way is to add a new property to IdentityUser:
public class ApplicationUser : IdentityUser
{
public PasswordHashVersion HashVersion { get; set; }
public ApplicationUser()
{
this.HashVersion = PasswordHashVersion.Core;
}
}
public enum PasswordHashVersion
{
OldMvc,
Core
}
现有用户的默认PasswordHashVersion
等于零(OldMvc),新注册的用户默认为1(Core).如果您有一种更聪明的方法来检测哈希是来自新算法还是旧算法,则不需要此方法.
Existing users will have default PasswordHashVersion
equals zero (OldMvc), new registered users will default to one (Core). If you have a smarter way to detect if a hash is from new or old algorithms, you don't need this.
然后,我们创建一个自定义的PasswordHash,它使用旧的默认哈希算法实现:
Then we create a custom PasswordHash, which uses the old default hash algorithm implementation:
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
中调用使用新算法的基本方法即可.否则,请使用旧的身份算法来验证密码.
This class inherits the new Identity Core PasswordHasher
. If the user's password hash version is already using the new algorithm (e.g HashVersion = Core), then we just call the base method from PasswordHasher
which uses the new algorithm. Otherwise, use the old identity algorithm to verify the password.
如果密码匹配,我们将用户密码哈希版本更新为Core
,然后返回PasswordVerificationResult.SuccessRehashNeeded
强制使用新算法更新现有哈希.
If the password matches, we update the user password hash version to Core
, and return PasswordVerificationResult.SuccessRehashNeeded
to force updating the existing hash with the new algorithm.
最后,您需要确保正在使用您的自定义PasswordHasher
.将此添加到ConfigureServices
内的Startup.cs
:
Lastly, you need to make sure your custom PasswordHasher
is being used. Add this to Startup.cs
inside ConfigureServices
:
// Replace the existing scoped IPasswordHasher<> implementation
services.Replace(new ServiceDescriptor(
serviceType: typeof(IPasswordHasher<ApplicationUser>),
implementationType: typeof(OldMvcPasswordHasher),
ServiceLifetime.Scoped));
必须在对
AddIdentity
,AddDefaultIdentity
或AddIdentityCore
的任何调用之后添加.
This must be added after any calls to
AddIdentity
,AddDefaultIdentity
orAddIdentityCore
.
这将在您的用户进行身份验证时缓慢更新密码哈希.
This will slowly update password hashes as your users authenticate.