作者: Isaac Levin
本文演示如何使用成员身份身份验证将 ASP.NET 应用的数据库架构迁移到 ASP.NET Core 2.0 Identity。
注释
本文档提供了将基于 ASP.NET 成员身份的应用的数据库架构迁移到用于 ASP.NET Core Identity的数据库架构所需的步骤。 有关从基于成员身份的 ASP.NET 身份验证迁移到 ASP.NET Identity的详细信息,请参阅 将现有应用从 SQL 成员身份迁移到 ASP.NET Identity。 有关 ASP.NET Core Identity的详细信息,请参阅 ASP.NET Core 简介Identity。
查看成员身份架构
在 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 遵循 Identity ASP.NET 4.5 中引入的原则。 虽然原则是共享的,但框架之间的实现是不同的,即使在 ASP.NET Core 的不同版本之间也是如此(请参阅 将身份验证和其他内容迁移到 ASP.NET Core 2.0)。
查看 ASP.NET Core 2.0 Identity 架构的最快方法是创建新的 ASP.NET Core 2.0 应用。 请按以下步骤在 Visual Studio 2017 中进行:
选择“文件”>“新建”>“项目”。
创建名为 CoreIdentitySample 的新 ASP.NET Core Web 应用程序项目。
在下拉列表中选择 ASP.NET Core 2.0 ,然后选择 “Web 应用程序”。 此模板生成 Razor Pages 应用。 在单击“ 确定”之前,单击“ 更改身份验证”。
为模板选择Identity。 最后,单击“ 确定”,然后单击 “确定”。 Visual Studio 使用 ASP.NET Core Identity 模板创建项目。
选择“工具>NuGet 包管理器>控制台”以打开“包管理器控制台”(PMC)窗口。
导航到 PMC 中的项目根目录,并运行 Entity Framework (EF) Core
Update-Database命令。ASP.NET Core 2.0 Identity 用于 EF Core 与存储身份验证数据的数据库进行交互。 为了使新创建的应用正常工作,需要有一个数据库来存储此数据。 创建新应用后,检查数据库环境中的架构的最快方法是使用 EF Core 迁移创建数据库。 此过程会在本地或其他地方创建一个数据库,该数据库模拟该架构。 有关详细信息,请查看前面的文档。
EF Core 命令使用在
appsettings.json中指定的数据库的连接字符串。 以下连接字符串面向名为 asp-net-core-identity 的 localhost 上的数据库。 在此设置中, EF Core 配置为使用DefaultConnection连接字符串。{ "ConnectionStrings": { "DefaultConnection": "Server=localhost;Database=aspnet-core-identity;Trusted_Connection=True;MultipleActiveResultSets=true" } }
警告
本文介绍连接字符串的使用。 使用本地数据库时,用户无需进行身份验证,但在生产环境中,连接字符串有时包括进行身份验证的密码。 资源所有者密码凭据(ROPC)是在生产数据库中应避免的安全风险。 生产应用应使用可用的最安全的身份验证流。 有关部署到测试或生产环境的应用的身份验证的详细信息,请参阅 安全身份验证流。
选择 “查看>SQL Server 对象资源管理器”。 展开与数据库名称
ConnectionStrings:DefaultConnection相对应的appsettings.json节点,该名称是在appsettings.json属性中指定的。该
Update-Database命令使用架构和应用初始化所需的任何数据创建了指定的数据库。 下图描述了使用上述步骤创建的表结构。
迁移架构
成员资格和 ASP.NET 核心 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 |
IsLockedOut 不映射到 LockoutEnabled。
IsLockedOut 被设置为用户登录失败次数过多而被锁定,时间为设定的时长。
LockoutEnabled 启用功能,可以锁定因登录失败次数过多的用户。 当用户尝试登录失败时, LockoutEnd 将设置为将来的日期,并且用户无法在该日期之前登录。 如果 LockoutEnabled 为 false,则用户永远不会因尝试登录失败而被锁定。 根据 OWASP, 在多次尝试失败后临时帐户锁定对于针对合法用户的 DoS 攻击来说太简单。
有关锁定的详细信息,请参阅 适用于弱锁定机制的 OWASP 测试。
迁移到 Identity 想要启用失败登录锁定的应用应在迁移过程中设置为 LockoutEnabled true。
注释
并非所有的字段映射都类似于成员资格与 ASP.NET Core (Identity) 之间的一对一关系。 上表采用默认成员身份用户架构并将其映射到 ASP.NET 核心 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 成员身份架构和数据。 另一个 CoreIdentitySample 数据库是使用前面所述的步骤创建的。 注释直接嵌入提供更多详细信息。
-- 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中,删除电子邮件属性中的[EmailAddress]属性。 将其重命名为 UserName。 这需要在提到EmailAddress的任何地方以及View和PageModel中进行更改。 结果如下所示:
后续步骤
本教程介绍了如何将用户从 SQL 成员身份移植到 ASP.NET Core 2.0 Identity。 有关 ASP.NET Core Identity的详细信息,请参阅 简介 Identity。