[英]Use a Identity 2.0 Database to Authenticate a ASP.NET Core 1.0 application
I am trying to a create a new ASP.NET Core 1.0 web application and I want it to use the Authentication tables that I already have set up.我正在尝试创建一个新的 ASP.NET Core 1.0 Web 应用程序,我希望它使用我已经设置的身份验证表。 These tables were originally created by a ASP.NET 4.6 web application that used Microsoft.ASPNet.Identity.EntityFramework 2.2.0这些表最初是由使用 Microsoft.ASPNet.Identity.EntityFramework 2.2.0 的 ASP.NET 4.6 Web 应用程序创建的
It looks like things have change in Microsoft.AspNetCore.Identity.EntityFrameworkCore because the new Core 1.0 application throws this error when trying to log in:看起来 Microsoft.AspNetCore.Identity.EntityFrameworkCore 中的事情发生了变化,因为新的 Core 1.0 应用程序在尝试登录时会抛出此错误:
A database operation failed while processing the request.处理请求时数据库操作失败。
SqlException: Invalid column name 'NormalizedUserName'. SqlException: 无效的列名“NormalizedUserName”。 Invalid column name 'ConcurrencyStamp'.无效的列名“ConcurrencyStamp”。 Invalid column name 'LockoutEnd'.无效的列名“LockoutEnd”。 Invalid column name 'NormalizedEmail'.无效的列名“NormalizedEmail”。 Invalid column name 'NormalizedUserName'.无效的列名“NormalizedUserName”。
The project.json is straight out of the box and looks like this: 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"
}
}, },
Also, I have not changed my ApplicationDbContext, but I have seen some posts about making changes to resolve this issue in that class另外,我没有更改我的 ApplicationDbContext,但我看到了一些关于在该类中进行更改以解决此问题的帖子
This SQL migration script got me over the above hurdle:这个 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
There's not been a ton of guidance from Microsoft over how to migrate the database but this fixed the above issue for me.微软并没有就如何迁移数据库提供大量指导,但这为我解决了上述问题。
There's a package available to do this.有一个包可以做到这一点。 It literally is for this exact purpose.从字面上看,它就是为了这个确切的目的。 It is part of Microsoft's codebase, and appears to have been updated recently for Core 2.1.它是 Microsoft 代码库的一部分,最近似乎已针对 Core 2.1 进行了更新。
A compatibility layer for sharing identity databases between Microsoft.AspNet.Identity.EntityFramework and Microsoft.AspNetCore.Identity.EntityFrameworkCore.用于在 Microsoft.AspNet.Identity.EntityFramework 和 Microsoft.AspNetCore.Identity.EntityFrameworkCore 之间共享身份数据库的兼容层。
https://www.nuget.org/packages/Microsoft.AspNet.Identity.AspNetCoreCompat/ https://www.nuget.org/packages/Microsoft.AspNet.Identity.AspNetCoreCompat/
https://github.com/aspnet/Identity/tree/master/src/AspNetCoreCompat https://github.com/aspnet/Identity/tree/master/src/AspNetCoreCompat
It handles the 'differences' between the two schemas with methods like this:它使用以下方法处理两个模式之间的“差异”:
/// <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();
I cannot find any real documentation, but I've done the following and it seems to be working ok:我找不到任何真正的文档,但我已经完成了以下操作并且它似乎工作正常:
You must change ALL references to IdentityRole
, IdentityUser
, IdentityDbContext
etc. to be the classes from the compatibility package.您必须将所有对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);
You have to upgrade your database to the new format (basically adding a few columns and changing some data types).您必须将数据库升级到新格式(基本上是添加几列并更改一些数据类型)。 This is the trickiest stage!这是最棘手的阶段! You're going to want to do this on a staging environment for sure.您肯定希望在登台环境中执行此操作。 I'm using Azure so I just cloned the DB.我正在使用 Azure,所以我只是克隆了数据库。
Migration.zip
.在 GitHub 上,我发现了一些由@samnpathdr编写的迁移脚本,称为Migration.zip
。 There are several scripts to be run one by one.有几个脚本要一一运行。 I'd recommend running each command one at a time to make sure it all runs.我建议一次运行一个命令以确保它全部运行。AspNetUsers
table, you must drop constraints, update the datatypes and add the constraints back.如果您有任何其他表引用AspNetUsers
表,则必须删除约束、更新数据类型并重新添加约束。 For example I have a Notes
table which is linked to a AspNetUser
so I needed to run ALTER TABLE UserProfileNote ALTER COLUMN AspNetUsersId nvarchar(450) NOT NULL;
例如,我有一个链接到AspNetUser
的Notes
表,所以我需要运行ALTER TABLE UserProfileNote ALTER COLUMN AspNetUsersId nvarchar(450) NOT NULL;
(after removing constraints). (删除约束后)。 Script the constraints first!首先编写约束脚本!https://github.com/aspnet/Docs/issues/6425 https://github.com/aspnet/Docs/issues/6425
I manually wrote a migration from the old to the new Identity
and the application works with both old and new users.我手动编写了从旧Identity
到新Identity
的迁移,该应用程序适用于新老用户。 Here's the migration if you want to save yourselves some manual work:如果您想为自己节省一些手动工作,请执行以下迁移:
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");
}
}
Also keep in mind that is one way migration.还要记住,这是一种迁移方式。 To be able still use old solution you could force .net core to use old password hashing algo为了仍然能够使用旧的解决方案,您可以强制 .net 核心使用旧密码散列算法
services.Configure<PasswordHasherOptions>(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.