且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何将身份用户从MVC5应用程序迁移到ASP.NET Core 2.2应用程序

更新时间: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));

必须在对AddIdentityAddDefaultIdentityAddIdentityCore的任何调用之后添加.

This must be added after any calls to AddIdentity, AddDefaultIdentity or AddIdentityCore.

这将在您的用户进行身份验证时缓慢更新密码哈希.

This will slowly update password hashes as your users authenticate.