本文演示如何使用成员资格身份验证将 ASP.NET 应用的数据库架构迁移到 ASP.NET Core 2.0 Identity。
备注
本文档提供将基于 ASP.NET 成员资格应用的数据库架构迁移到用于 ASP.NET Core Identity 的数据库架构所需的步骤。 有关从基于 ASP.NET 成员资格的身份验证迁移到 ASP.NET Identity 的详细信息,请参阅 Migrate an existing app from SQL Membership to ASP.NET Identity。 有关 ASP.NET Core Identity的更多信息,请参见Introduction to Identity on ASP.NET Core。
成员资格架构评审
在 ASP.NET 2.0 之前,开发人员的任务是为应用创建完整的身份验证和授权过程。 ASP.NET 2.0 中引入了成员资格,提供了一种样板解决方案来处理 ASP.NET 应用中的安全性。 开发人员现在能够使用 ASP.NET SQL Server 注册工具 (Aspnet_regsql.exe
)(不再受支持)将架构启动到 SQL Server 数据库中。 运行此命令后,数据库中会创建以下表。
若要将现有应用迁移到 ASP.NET Core 2.0 Identity,需要将这些表中的数据迁移到新的 Identity 架构使用的表。
ASP.NET Core Identity 2.0 架构
ASP.NET Core 2.0 遵循 ASP.NET 4.5 中引入的 Identity 原则。 尽管原则是共享的,但框架之间的实现有所不同,即使在不同版本的 ASP.NET Core 之间(请参阅 Migrate authentication and Identity to ASP.NET Core 2.0)。
查看 ASP.NET Core 2.0 Identity 的架构的最快方法是创建新的 ASP.NET Core 2.0 应用。 在 Visual Studio 2017 中执行以下步骤:
- 选择“文件”“新建”“项目”。
- 创建名为 CoreSample 的新 ASP.NET Core Web 应用程序。
- 在下拉列表中选择“ASP.NET Core 2.0”,然后选择“Web 应用程序”。 此模板会生成 Razor Pages 应用。 在单击“确定”之前,单击“更改身份验证”。
- 为 模板选择“单个用户帐户”。 最后,单击“确定”,然后选择“确定”。 Visual Studio 使用 ASP.NET Core Identity 模板创建项目。
- 依次选择“工具”“NuGet 包管理器”“包管理器控制台”,打开“包管理器控制台”(PMC) 窗口。
- 导航到 PMC 中的项目根目录,并运行 Entity Framework (EF) Core 命令。ASP.NET Core 2.0 Identity 使用 EF Core 与存储身份验证数据的数据库进行交互。 若要使新创建的应用正常工作,需要有一个数据库来存储此数据。 创建新应用后,检查数据库环境中架构的最快方法就是使用 EF Core 迁移创建数据库。 此过程会在本地或其他位置创建数据库以模拟该架构。 有关详细信息,请查看前面的文档。EF Core 命令使用 appsettings.json 中指定的数据库的连接字符串。 以下连接字符串针对 localhost 上名为 asp-net-core-identity 的数据库。 在此设置中,EF Core 配置为使用
DefaultConnection
连接字符串。
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=aspnet-core-identity;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
- 选择“视图”“SQL Server 对象资源管理器”。 展开与 appsettings.json 的
ConnectionStrings:DefaultConnection
属性中指定的数据库名称对应的节点。Update-Database
命令创建了使用架构指定的数据库以及应用初始化所需的任何数据。 下图描述了使用上述步骤创建的表结构。
Identity
- Tables" data-linktype="relative-path"/>
迁移架构
成员资格和 ASP.NET Core Identity 的表结构和字段存在细微差异。 使用 ASP.NET 和 ASP.NET Core 应用进行身份验证/授权的模式发生了重大变化。 仍与 Identity 一起使用的关键对象是“用户”和“角色”。 下面是“用户”、“角色”和“UserRoles”的映射表。
用户
Identity ( dbo.AspNetUsers ) 列 | 类型 | Membership ( dbo.aspnet_Users / dbo.aspnet_Membership ) 列 | 类型 |
---|---|---|---|
Id | string | aspnet_Users.UserId | string |
UserName | string | aspnet_Users.UserName | string |
Email | string | aspnet_Membership.Email | string |
NormalizedUserName | string | aspnet_Users.LoweredUserName | string |
NormalizedEmail | string | aspnet_Membership.LoweredEmail | string |
PhoneNumber | string | aspnet_Users.MobileAlias | string |
LockoutEnabled | bit | aspnet_Membership.IsLockedOut | bit |
备注
并非所有字段映射都类似于从成员身份到 ASP.NET Core Identity 的一对一关系。 上表采用默认的成员资格用户架构,并将其映射到 ASP.NET Core Identity 架构。 用于成员资格的任何其他自定义字段均需要手动映射。 在此映射中,没有对密码进行映射,因为密码标准和密码加盐不会在这两者之间迁移。 建议将密码保留为 null,并要求用户重置密码。 在 ASP.NET Core Identity 中,如果用户被锁定,应将 LockoutEnd
设置为将来的某一日期。迁移脚本中显示了这一点。
角色
Identity ( dbo.AspNetRoles ) 列 | 类型 | Membership ( dbo.aspnet_Roles ) 列 | 类型 |
---|---|---|---|
Id | string | RoleId | string |
Name | string | RoleName | string |
NormalizedName | string | LoweredRoleName | string |
用户角色
Identity ( dbo.AspNetUserRoles ) 列 | 类型 | Membership ( dbo.aspnet_UsersInRoles ) 列 | 类型 |
---|---|---|---|
RoleId | string | RoleId | string |
UserId | string | UserId | string |
为用户和角色创建迁移脚本时,请引用前面的映射表。 以下示例假定数据库服务器上有两个数据库。 一个数据库包含现有的 ASP.NET 成员身份架构和数据。 另一个 CoreSample 数据库是使用前面所述的步骤创建的。 注释以内联形式包含,用于提供更多详细信息。
-- THIS SCRIPT NEEDS TO RUN FROM THE CONTEXT OF THE MEMBERSHIP DB
BEGIN TRANSACTION MigrateUsersAndRoles
USE aspnetdb
-- INSERT USERS
INSERT INTO CoreIdentitySample.dbo.AspNetUsers
(Id,
UserName,
NormalizedUserName,
PasswordHash,
SecurityStamp,
EmailConfirmed,
PhoneNumber,
PhoneNumberConfirmed,
TwoFactorEnabled,
LockoutEnd,
LockoutEnabled,
AccessFailedCount,
Email,
NormalizedEmail)
SELECT aspnet_Users.UserId,
aspnet_Users.UserName,
-- The NormalizedUserName value is upper case in ASP.NET Core Identity
UPPER(aspnet_Users.UserName),
-- Creates an empty password since passwords don't map between the 2 schemas
'',
/*
The SecurityStamp token is used to verify the state of an account and
is subject to change at any time. It should be initialized as a new ID.
*/
NewID(),
/*
EmailConfirmed is set when a new user is created and confirmed via email.
Users must have this set during migration to reset passwords.
*/
1,
aspnet_Users.MobileAlias,
CASE
WHEN aspnet_Users.MobileAlias IS NULL THEN 0
ELSE 1
END,
-- 2FA likely wasn't setup in Membership for users, so setting as false.
0,
CASE
-- Setting lockout date to time in the future (1,000 years)
WHEN aspnet_Membership.IsLockedOut = 1 THEN Dateadd(year, 1000,
Sysutcdatetime())
ELSE NULL
END,
aspnet_Membership.IsLockedOut,
/*
AccessFailedAccount is used to track failed logins. This is stored in
Membership in multiple columns. Setting to 0 arbitrarily.
*/
0,
aspnet_Membership.Email,
-- The NormalizedEmail value is upper case in ASP.NET Core Identity
UPPER(aspnet_Membership.Email)
FROM aspnet_Users
LEFT OUTER JOIN aspnet_Membership
ON aspnet_Membership.ApplicationId =
aspnet_Users.ApplicationId
AND aspnet_Users.UserId = aspnet_Membership.UserId
LEFT OUTER JOIN CoreIdentitySample.dbo.AspNetUsers
ON aspnet_Membership.UserId = AspNetUsers.Id
WHERE AspNetUsers.Id IS NULL
-- INSERT ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetRoles(Id, Name)
SELECT RoleId, RoleName
FROM aspnet_Roles;
-- INSERT USER ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetUserRoles(UserId, RoleId)
SELECT UserId, RoleId
FROM aspnet_UsersInRoles;
IF @@ERROR <> 0
BEGIN
ROLLBACK TRANSACTION MigrateUsersAndRoles
RETURN
END
COMMIT TRANSACTION MigrateUsersAndRoles
完成上述脚本后,将为前面创建的 ASP.NET Core Identity 应用填充成员资格用户。 用户需要在登录之前更改其密码。
备注
如果成员资格系统中用户的用户名与电子邮件地址不匹配,则需要更改之前创建的应用以适应这种情况。 默认模板预期 UserName
和 Email
是相同的。 对于它们不同的情况,需要修改登录过程以使用 UserName
而不是 Email
。
在登录页的 PageModel
中,找到 Pages\Account\Login.cshtml.cs,然后从“Email”属性中删除 [EmailAddress]
特性。 将其重命名为“UserName”。 这需要在“视图”和“PageModel”中任何提到 EmailAddress
的位置进行更改。 结果应类似如下所示:
后续步骤
本教程介绍了如何将用户从 SQL 成员身份移植到 ASP.NET Core 2.0 Identity。 有关 ASP.NET Core Identity 的详细信息,请参阅 Introduction to Identity。