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

Glo*_*Jim 5 .net c# asp.net-mvc asp.net-core

我有一个使用Identity在MVC5上构建的Web应用程序。我想将此项目转换为ASP.NET Core 2.2 Web应用程序。我创建了一个新的ASP.NET Core 2.2 Web应用程序,其身份验证设置为“个人用户帐户”,并按照以下说明将数据库添加到了项目中。然后,我在项目中添加了一个新的Identity Scaffolded项目,并添加了迁移并更新了数据库。

我注册了一个测试用户,当我检查SQL管理服务器时,我看到它为该项目创建了一个新数据库,即使我的连接字符串适用于旧数据库。

我想保留我的旧数据库,但将其转换为使用ASP.NET Core内置的新Identity Razor页面。这样做的最佳方法是什么?

Ant*_*eia 8

我刚刚能够通过以下步骤成功地将 .NET 4.5.2 项目迁移到 .NET Core 3.1

  1. 用于Scaffold-DbContext根据现有数据库创建模型[1]
Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>] [-DataAnnotations] [-Force] [-Project] [-StartupProject] [<CommonParameters>]
Run Code Online (Sandbox Code Playgroud)
  1. 从生成的上下文中删除所有与 AspNet 相关的表以及不再需要的 .cs 文件。

  2. 添加到上下文文件上base.OnModelCreating(modelBuilder);生成的方法。[2]OnModelCreating

  3. 运行下面的脚本来更新/创建身份表[3] [4]

Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>] [-DataAnnotations] [-Force] [-Project] [-StartupProject] [<CommonParameters>]
Run Code Online (Sandbox Code Playgroud)
  1. Startup.cs文件上设置密码哈希器兼容模式以考虑 IdentityV2
ALTER TABLE ASPNETROLES
ADD
 ConcurrencyStamp VARCHAR(255) NULL,               
 NormalizedName VARCHAR(255) NULL

 DROP TABLE AspNetUserTokens

 CREATE TABLE [AspNetUserTokens] (
    [UserId]        NVARCHAR (450) NOT NULL,
    [LoginProvider] NVARCHAR (450) NOT NULL,
    [Name]          NVARCHAR (450) NOT NULL,
    [Value]         NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_AspNetUserTokens]
 PRIMARY KEY CLUSTERED ([UserId] ASC, [LoginProvider] ASC, [Name] ASC)
)

ALTER TABLE AspNetUsers
 ADD
 ConcurrencyStamp VARCHAR(255) NULL,
 LockoutEnd DATETIME NULL,
 NormalizedEmail VARCHAR(255) NULL,
 NormalizedUserName VARCHAR(255) NULL

DROP TABLE [AspNetRoleClaims]

CREATE TABLE [AspNetRoleClaims] (
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [ClaimType]  NVARCHAR (MAX) NULL,
    [ClaimValue] NVARCHAR (MAX) NULL,
    [RoleId]     NVARCHAR (128) NOT NULL,
    CONSTRAINT [PK_AspNetRoleClaims]
 PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId]
 FOREIGN KEY ([RoleId])
  REFERENCES [dbo].[AspNetRoles] ([Id]) ON DELETE CASCADE
)


GO
CREATE NONCLUSTERED INDEX [IX_AspNetRoleClaims_RoleId]
    ON [AspNetRoleClaims]([RoleId] ASC)

ALTER TABLE AspNetUserLogins
   ADD ProviderDisplayName VARCHAR(255) NULL

UPDATE AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(UserName)
WHERE NormalizedEmail IS NULL
Run Code Online (Sandbox Code Playgroud)


Ali*_*son 5

升级标识表后,您可能要更新现有用户的密码哈希。表中的一些新列AspNetUsers将具有NULL值。首先运行:

UPDATE AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(Email)
WHERE NormalizedEmail IS NULL
Run Code Online (Sandbox Code Playgroud)

实际上,NormalizedUserName已使用大写电子邮件进行更新。

我们需要一种方法来区分哪些用户正在使用新的哈希版本。

一种方法是向IdentityUser添加新属性:

public class ApplicationUser : IdentityUser
{
    public PasswordHashVersion HashVersion { get; set; }

    public ApplicationUser()
    {
        this.HashVersion = PasswordHashVersion.Core;
    }
}

public enum PasswordHashVersion
{
    OldMvc,
    Core
}
Run Code Online (Sandbox Code Playgroud)

现有用户的默认值将PasswordHashVersion为零(OldMvc),新注册的用户的默认值将为1(Core)。如果您有一种更聪明的方式来检测哈希是来自新算法还是旧算法,则不需要此方法。

然后,我们创建一个自定义的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

此类继承了新的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));
Run Code Online (Sandbox Code Playgroud)

这必须以任何呼叫后加入AddIdentityAddDefaultIdentityAddIdentityCore

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

  • 在我们的例子中,场景是同时使用新旧项目,因此我们需要像以前一样对密码进行哈希处理。这个答案的一个小改变解决了我们的问题。我们只是删除了与检测哈希版本和更新密码哈希相关的部分。 (3认同)
  • 我们正在从 Framework 4.7.2 上的 MVC 项目更新到 .NET 5.0,对于我们来说,我们只是复制现有表,但随后必须更新 AspNetUsers 表上的空字段(规范化用户名等)。然后在startup.cs中添加兼容性标志: services.Configure&lt;PasswordHasherOptions&gt;(options =&gt; options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2); &lt;-- 这让一切都为我们工作,而无需执行其他所有操作。 (3认同)

归档时间:

查看次数:

823 次

最近记录:

6 年,7 月 前