使用标识 2.0 数据库对 ASP.NET 核心 1.0 应用程序进行身份验证
本文关键字:应用程序 核心 身份验证 NET ASP 标识 数据库 | 更新日期: 2023-09-27 17:56:29
我正在尝试创建一个新的 ASP.NET Core 1.0 Web应用程序,我希望它使用我已经设置的身份验证表。 这些表最初是由使用 Microsoft.ASPNet.Identity.EntityFramework 2.2.0 的 ASP.NET 4.6 Web 应用程序创建的
。看起来Microsoft.AspNetCore.Identity.EntityFrameworkCore发生了变化。因为新的Core 1.0应用程序在尝试登录时会引发此错误:
处理请求时数据库操作失败。
SqlException:列名"规范化用户名"无效。 无效的列名"并发戳"。 无效的列名称"锁定结束"。 列名称"规范化电子邮件"无效。 无效的列名称"规范化用户名"。
project.json 是开箱即用的,看起来像这样:
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
},
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
"Microsoft.AspNetCore.Diagnostics": "1.0.0",
"Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0",
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Razor.Tools": {
"version": "1.0.0-preview2-final",
"type": "build"
},
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer.Design": {
"version": "1.0.0",
"type": "build"
},
"Microsoft.EntityFrameworkCore.Tools": {
"version": "1.0.0-preview2-final",
"type": "build"
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0",
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0",
"Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0",
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
"version": "1.0.0-preview2-final",
"type": "build"
},
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
"version": "1.0.0-preview2-final",
"type": "build"
}
},
另外,我没有更改我的ApplicationDbContext,但是我看到了一些关于在该类中进行更改以解决此问题的文章
这个SQL迁移脚本让我克服了上述障碍:
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
Microsoft没有关于如何迁移数据库的大量指导,但这为我解决了上述问题。
有一个包可用于执行此操作。从字面上看,它正是为了这个目的。它是Microsoft代码库的一部分,最近似乎针对Core 2.1进行了更新。
用于在 Microsoft.AspNet.Identity.EntityFramework 和 Microsoft.AspNetCore.Identity.EntityFrameworkCore 之间共享身份数据库的兼容层。
https://www.nuget.org/packages/Microsoft.AspNet.Identity.AspNetCoreCompat/
https://github.com/aspnet/Identity/tree/master/src/AspNetCoreCompat
它使用如下方法处理两个模式之间的"差异":
/// <summary>
/// Normalized email
/// </summary>
public string NormalizedEmail {
get
{
return Email.ToUpperInvariant();
}
set { }
}
/// <summary>
/// Concurrency stamp
/// </summary>
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
我找不到任何真正的文档,但是我已经完成了以下操作,并且似乎工作正常:
- 在旧网站 (.NET 4.6) 中安装兼容包
您必须将所有对
IdentityRole
、IdentityUser
、IdentityDbContext
等的引用更改为兼容包中的类。using Compat = Microsoft.AspNet.Identity.CoreCompat; // update to use the compatibility class public class ApplicationDbContext : Compat.IdentityDbContext<ApplicationUser> // change all instances, such as this Compat.IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
您必须将数据库升级到新格式(基本上是添加几列并更改一些数据类型)。这是最棘手的阶段!您肯定希望在暂存环境中执行此操作。我正在使用 Azure,所以我只是克隆了数据库。
- 在GitHub上,我发现了一些名为
Migration.zip
的@samnpathdr迁移脚本。有几个脚本要逐个运行。我建议一次运行一个命令,以确保它全部运行。 - 目前,他的脚本中有一个表是他的实现(AspNetUserRolePermissions)自定义的,因此请删除对该表的引用。
- 如果有任何其他表引用
AspNetUsers
表,则必须删除约束,更新数据类型并重新添加约束。例如,我有一个链接到AspNetUser
的Notes
表,所以我需要运行ALTER TABLE UserProfileNote ALTER COLUMN AspNetUsersId nvarchar(450) NOT NULL;
(删除约束后)。首先编写约束脚本! - 如果您为 Core 启用了任何"自动迁移",请小心,因为就个人而言,在这种更改之后,我不会信任它们。应重置为基线,或者根本不执行 EF 迁移。
https://github.com/aspnet/Docs/issues/6425
我手动编写了从旧Identity
到新的迁移,该应用程序适用于新老用户。如果您想节省一些手动工作,这里是迁移:
public partial class Identity : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "NormalizedName",
table: "AspNetRoles",
type: "nvarchar(256)",
maxLength: 256,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "ConcurrencyStamp",
table: "AspNetRoles",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.RenameColumn(
name: "LockoutEndDateUtc",
table: "AspNetUsers",
newName: "LockoutEnd");
migrationBuilder.AddColumn<string>(
name: "ConcurrencyStamp",
table: "AspNetUsers",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "NormalizedEmail",
table: "AspNetUsers",
type: "nvarchar(256)",
maxLength: 256,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "NormalizedUsername",
table: "AspNetUsers",
type: "nvarchar(256)",
maxLength: 256,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "ProviderDisplayName",
table: "AspNetUserLogins",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
RoleId = table.Column<string>(type: "nvarchar(128)", nullable: false),
ClaimType = table.Column<string>(type: "nvarchar(max)", nullable: true),
ClaimValue = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(type: "nvarchar(128)", nullable: false),
LoginProvider = table.Column<string>(type: "nvarchar(450)", nullable: false),
Name = table.Column<string>(type: "nvarchar(450)", nullable: false),
Value = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ConcurrencyStamp",
table: "AspNetRoles");
migrationBuilder.DropColumn(
name: "NormalizedName",
table: "AspNetRoles");
migrationBuilder.RenameColumn(
name: "LockoutEnd",
table: "AspNetUsers",
newName: "LockoutEndDateUtc");
migrationBuilder.DropColumn(
name: "ConcurrencyStamp",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "NormalizedEmail",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "NormalizedUsername",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "ProviderDisplayName",
table: "AspNetUserLogins");
migrationBuilder.DropTable("AspNetRoleClaims");
migrationBuilder.DropTable("AspNetUserTokens");
}
}
还要记住,这是单向迁移。为了能够仍然使用旧的解决方案,您可以强制.net核心使用旧的密码哈希算法
services.Configure<PasswordHasherOptions>(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2);