Migrieren einer vorhandenen Website von einem SQL-Mitgliedschaftsanbieter zu ASP.NET Identity

von Rick Anderson, Suhas Joshi

In diesem Tutorial werden die Schritte zum Migrieren einer vorhandenen Webanwendung mit Benutzer- und Rollendaten veranschaulicht, die mithilfe der SQL-Mitgliedschaft erstellt wurden, zum neuen ASP.NET Identity-System. Dieser Ansatz umfasst das Ändern des vorhandenen Datenbankschemas in das vom ASP.NET Identity erforderliche Schema und das Einbinden der alten/neuen Klassen daran. Nachdem Sie diesen Ansatz übernommen haben, werden nach der Migration Ihrer Datenbank zukünftige Aktualisierungen von Identity mühelos verarbeitet.

In diesem Tutorial verwenden wir eine Webanwendungsvorlage (Web Forms), die mit Visual Studio 2010 erstellt wurde, um Benutzer- und Rollendaten zu erstellen. Anschließend werden SQL-Skripts verwendet, um die vorhandene Datenbank in Tabellen zu migrieren, die vom Identitätssystem benötigt werden. Als Nächstes installieren wir die erforderlichen NuGet-Pakete und fügen neue Kontoverwaltungsseiten hinzu, die das Identitätssystem für die Mitgliedschaftsverwaltung verwenden. Als Test der Migration sollten sich Benutzer, die mithilfe der SQL-Mitgliedschaft erstellt wurden, anmelden können, und neue Benutzer sollten in der Lage sein, sich zu registrieren. Das vollständige Beispiel finden Sie hier. Weitere Informationen finden Sie unter Migrieren von ASP.NET Mitgliedschaft zu ASP.NET Identität.

Erste Schritte

Erstellen einer Anwendung mit SQL-Mitgliedschaft

  1. Wir müssen mit einer vorhandenen Anwendung beginnen, die die SQL-Mitgliedschaft verwendet und Benutzer- und Rollendaten enthält. Für diesen Artikel erstellen wir eine Webanwendung in Visual Studio 2010.

    Screenshot: Erstellen einer Webanwendung in Visual Studio 2010

  2. Erstellen Sie mit dem ASP.NET-Konfigurationstool 2 Benutzer: oldAdminUser und oldUser.

    Screenshot: A S P . N E T-Konfigurationstool zum Erstellen von zwei Benutzern.

    Screenshot des Website-Verwaltungstools zum Verwalten der gesamten Sicherheit.

  3. Erstellen Sie eine Rolle namens Admin, und fügen Sie "oldAdminUser" als Benutzer in dieser Rolle hinzu.

    Screenshot: Admin Rolle, die als Benutzer in dieser Rolle erstellt wurde.

  4. Erstellen Sie einen Admin Abschnitt der Website mit einer Default.aspx. Legen Sie das Autorisierungstag in der web.config-Datei fest, um den Zugriff nur für Benutzer in Admin Rollen zu ermöglichen. Weitere Informationen finden Sie hier. https://www.asp.net/web-forms/tutorials/security/roles/role-based-authorization-cs

    Screenshot des Admin Abschnitts der Website

  5. Zeigen Sie die Datenbank in Server Explorer an, um die vom SQL-Mitgliedschaftssystem erstellten Tabellen zu verstehen. Die Benutzeranmeldungsdaten werden in den tabellen aspnet_Users und aspnet_Membership gespeichert, während Rollendaten in der aspnet_Roles Tabelle gespeichert werden. Informationen darüber, welche Benutzer sich in welchen Rollen befinden, werden in der aspnet_UsersInRoles Tabelle gespeichert. Für die grundlegende Mitgliedschaftsverwaltung reicht es aus, die Informationen in den obigen Tabellen in das ASP.NET Identity-System zu portieren.

    Screenshot: Server Explorer, um die Datenbank anzuzeigen, um die vom S Q L-Mitgliedschaftssystem erstellten Tabellen zu verstehen.

Migrieren zu Visual Studio 2013

  1. Installieren Sie Visual Studio Express 2013 für Web oder Visual Studio 2013 zusammen mit den neuesten Updates.

  2. Öffnen Sie das obige Projekt in Ihrer installierten Version von Visual Studio. Wenn SQL Server Express nicht auf dem Computer installiert ist, wird beim Öffnen des Projekts eine Eingabeaufforderung angezeigt, da die Verbindungszeichenfolge SQL Express verwendet. Sie können entweder SQL Express installieren oder die Verbindungszeichenfolge in LocalDb ändern. Für diesen Artikel ändern wir ihn in LocalDb.

  3. Öffnen Sie web.config, und ändern Sie die Verbindungszeichenfolge von . SQLExpress zu (LocalDb)v11.0. Entfernen Sie "User Instance=true" aus der Verbindungszeichenfolge.

    Screenshot: Ändern der Verbindungszeichenfolge für die Migration zu Visual Studio 2013

  4. Öffnen Sie server Explorer, und vergewissern Sie sich, dass das Tabellenschema und die Daten beobachtet werden können.

  5. Das ASP.NET Identity-System funktioniert mit Version 4.5 oder höher des Frameworks. Suchen Sie die Anwendung erneut auf 4.5 oder höher.

    Screenshot: A S P . N E T Identitätssystem.

    Erstellen Sie das Projekt, um sicherzustellen, dass keine Fehler vorliegen.

Installieren der Nuget-Pakete

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >NuGet-Pakete verwalten. Geben Sie in das Suchfeld "Asp.net Identity" ein. Wählen Sie das Paket in der Ergebnisliste aus, und klicken Sie auf Installieren. Akzeptieren Sie den Lizenzvertrag, indem Sie auf die Schaltfläche "Ich stimme zu" klicken. Beachten Sie, dass dieses Paket die Abhängigkeitspakete EntityFramework und Microsoft ASP.NET Identity Core installiert. Installieren Sie auf ähnliche Weise die folgenden Pakete (überspringen Sie die letzten 4 OWIN-Pakete, wenn Sie die OAuth-Anmeldung nicht aktivieren möchten):

    • Microsoft.AspNet.Identity.Owin

    • Microsoft.Owin.Host.SystemWeb

    • Microsoft.Owin.Security.Facebook

    • Microsoft.Owin.Security.Google

    • Microsoft.Owin.Security.MicrosoftAccount

    • Microsoft.Owin.Security.Twitter

      Screenshot: Installation der Nuget-Pakete in Projektmappen-Explorer

Migrieren der Datenbank zum neuen Identitätssystem

Der nächste Schritt besteht darin, die vorhandene Datenbank zu einem Schema zu migrieren, das für das ASP.NET Identity-System erforderlich ist. Um dies zu erreichen, führen wir ein SQL-Skript aus, das über eine Reihe von Befehlen verfügt, um neue Tabellen zu erstellen und vorhandene Benutzerinformationen in die neuen Tabellen zu migrieren. Die Skriptdatei finden Sie hier.

Diese Skriptdatei ist spezifisch für dieses Beispiel. Wenn das Schema für die mithilfe der SQL-Mitgliedschaft erstellten Tabellen angepasst oder geändert wird, müssen die Skripts entsprechend geändert werden.

Generieren des SQL-Skripts für die Schemamigration

Damit ASP.NET Identity-Klassen mit den Daten vorhandener Benutzer sofort einsatzbereit sind, müssen wir das Datenbankschema zu dem migrieren, das von ASP.NET Identity benötigt wird. Dazu können sie neue Tabellen hinzufügen und die vorhandenen Informationen in diese Tabellen kopieren. Standardmäßig verwendet ASP.NET Identity EntityFramework, um die Identitätsmodellklassen wieder der Datenbank zuzuordnen, um Informationen zu speichern/abzurufen. Diese Modellklassen implementieren die Zentralen Identitätsschnittstellen, die Benutzer- und Rollenobjekte definieren. Die Tabellen und Spalten in der Datenbank basieren auf diesen Modellklassen. Die EntityFramework-Modellklassen in Identity v2.1.0 und ihre Eigenschaften sind wie unten definiert.

IdentityUser Typ IdentityRole IdentityUserRole IdentityUserLogin IdentityUserClaim
Id string Id RoleId ProviderKey Id
Username Zeichenfolge Name UserId UserId ClaimType
PasswordHash Zeichenfolge LoginProvider ClaimValue
SecurityStamp Zeichenfolge User_id
E-Mail Zeichenfolge
EmailConfirmed bool
PhoneNumber Zeichenfolge
PhoneNumberConfirmed bool
LockoutEnabled bool
LockoutEndDate Datetime
AccessFailedCount INT

Für jedes dieser Modelle müssen Tabellen mit Spalten vorhanden sein, die den Eigenschaften entsprechen. Die Zuordnung zwischen Klassen und Tabellen wird in der OnModelCreating -Methode von IdentityDBContextdefiniert. Dies wird als Fluent-API-Konfigurationsmethode bezeichnet, und weitere Informationen finden Sie hier. Die Konfiguration für die Klassen ist wie unten erwähnt.

Klasse Tabelle Primärschlüssel Fremdschlüssel
IdentityUser AspnetUsers Id
IdentityRole AspnetRoles Id
IdentityUserRole AspnetUserRole UserId + RoleId User_Id-AspnetUsers> RoleId-AspnetRoles>
IdentityUserLogin AspnetUserLogins ProviderKey+UserId + LoginProvider UserId-AspnetUsers>
IdentityUserClaim AspnetUserClaims Id User_Id-AspnetUsers>

Mit diesen Informationen können wir SQL-Anweisungen erstellen, um neue Tabellen zu erstellen. Wir können jede Anweisung entweder einzeln schreiben oder das gesamte Skript mithilfe von EntityFramework PowerShell-Befehlen generieren, die wir dann nach Bedarf bearbeiten können. Öffnen Sie hierzu in VS die Paket-Manager-Konsole über das Menü Ansicht oder Extras .

  • Führen Sie den Befehl "Enable-Migrations" aus, um EntityFramework-Migrationen zu aktivieren.
  • Führen Sie den Befehl "Add-migration initial" aus, der den anfänglichen Setupcode zum Erstellen der Datenbank in C#/VB erstellt.
  • Der letzte Schritt besteht darin, den Befehl "Update-Database –Script" auszuführen, der das SQL-Skript basierend auf den Modellklassen generiert.

Einige Befehle werden nicht unterstützt, wenn die App SQLite als Identitätsdatenspeicher verwendet. Aufgrund von Einschränkungen in der Datenbank-Engine Alter lösen Befehle die folgende Ausnahme aus:

"System.NotSupportedException: SQLite unterstützt diesen Migrationsvorgang nicht."

Führen Sie code first-Migrationen für die Datenbank aus, um die Tabellen zu ändern.

Dieses Datenbankgenerierungsskript kann als Ausgangspunkt verwendet werden, bei dem wir zusätzliche Änderungen vornehmen, um neue Spalten hinzuzufügen und Daten zu kopieren. Der Vorteil besteht darin, dass wir die Tabelle generieren, die _MigrationHistory von EntityFramework verwendet wird, um das Datenbankschema zu ändern, wenn sich die Modellklassen für zukünftige Versionen von Identity-Releases ändern.

Die Benutzerinformationen für die SQL-Mitgliedschaft verfügten zusätzlich zu den Informationen in der Identitätsbenutzermodellklasse über weitere Eigenschaften: E-Mail, Kennwortversuche, Datum der letzten Anmeldung, Datum der letzten Sperrung usw. Dies sind nützliche Informationen, und wir möchten, dass sie in das Identitätssystem übernommen werden. Dazu können dem Benutzermodell zusätzliche Eigenschaften hinzugefügt und den Tabellenspalten in der Datenbank zugeordnet werden. Dazu können Sie eine Klasse hinzufügen, die das IdentityUser Modell unterklassiert. Wir können dieser benutzerdefinierten Klasse die Eigenschaften hinzufügen und das SQL-Skript bearbeiten, um beim Erstellen der Tabelle die entsprechenden Spalten hinzuzufügen. Der Code für diese Klasse wird im Artikel weiter beschrieben. Das SQL-Skript zum Erstellen der AspnetUsers Tabelle nach dem Hinzufügen der neuen Eigenschaften wäre

CREATE TABLE [dbo].[AspNetUsers] (
    [Id]            NVARCHAR (128) NOT NULL,
    [UserName]      NVARCHAR (MAX) NULL,
    [PasswordHash]  NVARCHAR (MAX) NULL,
    [SecurityStamp] NVARCHAR (MAX) NULL,
    [EmailConfirmed]       BIT            NOT NULL,
    [PhoneNumber]          NVARCHAR (MAX) NULL,
    [PhoneNumberConfirmed] BIT            NOT NULL,
    [TwoFactorEnabled]     BIT            NOT NULL,
    [LockoutEndDateUtc]    DATETIME       NULL,
    [LockoutEnabled]       BIT            NOT NULL,
    [AccessFailedCount]    INT            NOT NULL,
    [ApplicationId]                          UNIQUEIDENTIFIER NOT NULL,
    [LegacyPasswordHash]  NVARCHAR (MAX) NULL,
    [LoweredUserName]  NVARCHAR (256)   NOT NULL,
    [MobileAlias]      NVARCHAR (16)    DEFAULT (NULL) NULL,
    [IsAnonymous]      BIT              DEFAULT ((0)) NOT NULL,
    [LastActivityDate] DATETIME2         NOT NULL,
    [MobilePIN]                              NVARCHAR (16)    NULL,
    [Email]                                  NVARCHAR (256)   NULL,
    [LoweredEmail]                           NVARCHAR (256)   NULL,
    [PasswordQuestion]                       NVARCHAR (256)   NULL,
    [PasswordAnswer]                         NVARCHAR (128)   NULL,
    [IsApproved]                             BIT              NOT NULL,
    [IsLockedOut]                            BIT              NOT NULL,
    [CreateDate]                             DATETIME2               NOT NULL,
    [LastLoginDate]                          DATETIME2         NOT NULL,
    [LastPasswordChangedDate]                DATETIME2         NOT NULL,
    [LastLockoutDate]                        DATETIME2         NOT NULL,
    [FailedPasswordAttemptCount]             INT              NOT NULL,
    [FailedPasswordAttemptWindowStart]       DATETIME2         NOT NULL,
    [FailedPasswordAnswerAttemptCount]       INT              NOT NULL,
    [FailedPasswordAnswerAttemptWindowStart] DATETIME2         NOT NULL,
    [Comment]                                NTEXT            NULL,
    CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC),
    FOREIGN KEY ([ApplicationId]) REFERENCES [dbo].[aspnet_Applications] ([ApplicationId]),
);

Als Nächstes müssen wir die vorhandenen Informationen aus der SQL-Mitgliedschaftsdatenbank in die neu hinzugefügten Tabellen für Identität kopieren. Dies kann über SQL erfolgen, indem Daten direkt aus einer Tabelle in eine andere kopiert werden. Zum Hinzufügen von Daten zu den Tabellenzeilen verwenden wir das INSERT INTO [Table] -Konstrukt. Zum Kopieren aus einer anderen Tabelle können wir die INSERT INTO -Anweisung zusammen mit der SELECT -Anweisung verwenden. Um alle Benutzerinformationen abzurufen, müssen wir die aspnet_Users abfragen und Tabellen aspnet_Membership und die Daten in die Tabelle AspNetUsers kopieren. Wir verwenden und INSERT INTOSELECT zusammen mit JOIN und LEFT OUTER JOIN -Anweisungen. Weitere Informationen zum Abfragen und Kopieren von Daten zwischen Tabellen finden Sie unter diesem Link. Darüber hinaus sind die Tabellen AspnetUserLogins und AspnetUserClaims zu Beginn leer, da es keine Informationen in der SQL-Mitgliedschaft gibt, die dieser standardmäßig zugeordnet sind. Die einzigen kopierten Informationen sind für Benutzer und Rollen. Für das in den vorherigen Schritten erstellte Projekt lautet die SQL-Abfrage zum Kopieren von Informationen in die Benutzertabelle.

INSERT INTO AspNetUsers(Id,UserName,PasswordHash,SecurityStamp,EmailConfirmed,
PhoneNumber,PhoneNumberConfirmed,TwoFactorEnabled,LockoutEndDateUtc,LockoutEnabled,AccessFailedCount,
ApplicationId,LoweredUserName,MobileAlias,IsAnonymous,LastActivityDate,LegacyPasswordHash,
MobilePIN,Email,LoweredEmail,PasswordQuestion,PasswordAnswer,IsApproved,IsLockedOut,CreateDate,
LastLoginDate,LastPasswordChangedDate,LastLockoutDate,FailedPasswordAttemptCount,
FailedPasswordAnswerAttemptWindowStart,FailedPasswordAnswerAttemptCount,FailedPasswordAttemptWindowStart,Comment)
SELECT aspnet_Users.UserId,aspnet_Users.UserName,(aspnet_Membership.Password+'|'+CAST(aspnet_Membership.PasswordFormat as varchar)+'|'+aspnet_Membership.PasswordSalt),NewID(),
'true',NULL,'false','true',aspnet_Membership.LastLockoutDate,'true','0',
aspnet_Users.ApplicationId,aspnet_Users.LoweredUserName,
aspnet_Users.MobileAlias,aspnet_Users.IsAnonymous,aspnet_Users.LastActivityDate,aspnet_Membership.Password,
aspnet_Membership.MobilePIN,aspnet_Membership.Email,aspnet_Membership.LoweredEmail,aspnet_Membership.PasswordQuestion,aspnet_Membership.PasswordAnswer,
aspnet_Membership.IsApproved,aspnet_Membership.IsLockedOut,aspnet_Membership.CreateDate,aspnet_Membership.LastLoginDate,aspnet_Membership.LastPasswordChangedDate,
aspnet_Membership.LastLockoutDate,aspnet_Membership.FailedPasswordAttemptCount, aspnet_Membership.FailedPasswordAnswerAttemptWindowStart,
aspnet_Membership.FailedPasswordAnswerAttemptCount,aspnet_Membership.FailedPasswordAttemptWindowStart,aspnet_Membership.Comment
FROM aspnet_Users
LEFT OUTER JOIN aspnet_Membership ON aspnet_Membership.ApplicationId = aspnet_Users.ApplicationId 
AND aspnet_Users.UserId = aspnet_Membership.UserId;

In der obigen SQL-Anweisung werden Informationen zu jedem Benutzer aus den tabellen aspnet_Users und aspnet_Membership in die Spalten der Tabelle AspnetUsers kopiert. Die einzige Änderung, die hier vorgenommen wird, ist das Kopieren des Kennworts. Da der Verschlüsselungsalgorithmus für Kennwörter in der SQL-Mitgliedschaft "PasswordSalt" und "PasswordFormat" verwendet hat, kopieren wir auch dieses Kennwort zusammen mit dem Hashkennwort, damit es zum Entschlüsseln des Kennworts nach Identität verwendet werden kann. Dies wird im Artikel beim Einbinden eines benutzerdefinierten Kennwort-Hashers weiter erläutert.

Diese Skriptdatei ist spezifisch für dieses Beispiel. Für Anwendungen mit zusätzlichen Tabellen können Entwickler einen ähnlichen Ansatz verwenden, um der Benutzermodellklasse zusätzliche Eigenschaften hinzuzufügen und spalten in der Tabelle AspnetUsers zuzuordnen. Führen Sie das Skript aus:

  1. Öffnen Sie Server-Explorer. Erweitern Sie die Verbindung "ApplicationServices", um die Tabellen anzuzeigen. Klicken Sie mit der rechten Maustaste auf den Knoten Tabellen, und wählen Sie die Option "Neue Abfrage" aus.

    Screenshot: Explorer zum Erweitern der Application Services-Verbindung zum Anzeigen der Tabellen

  2. Kopieren Sie im Abfragefenster das gesamte SQL-Skript aus der Datei Migrations.sql, und fügen Sie es ein. Führen Sie die Skriptdatei aus, indem Sie auf die Pfeilschaltfläche "Ausführen" klicken.

    Screenshot des Abfragefensters zum Kopieren und Einfügen des gesamten S Q L-Skripts.

    Aktualisieren Sie das Fenster Server Explorer. Fünf neue Tabellen werden in der Datenbank erstellt.

    Screenshot: Aktualisieren des Fensters

    Screenshot der Tabellen in Datenverbindungen.

    Nachfolgend sehen Sie, wie die Informationen in den SQL-Mitgliedschaftstabellen dem neuen Identitätssystem zugeordnet werden.

    aspnet_Roles :> AspNetRoles

    asp_netUsers und asp_netMembership –-> AspNetUsers

    aspnet_UserInRoles :> AspNetUserRoles

    Wie im obigen Abschnitt erläutert, sind die Tabellen AspNetUserClaims und AspNetUserLogins leer. Das Feld "Diskriminator" in der Tabelle AspNetUser sollte mit dem Modellklassennamen übereinstimmen, der als nächster Schritt definiert wird. Außerdem hat die Spalte PasswordHash die Form "encrypted password |password salt|password format". Dadurch können Sie eine spezielle Kryptologik für die SQL-Mitgliedschaft verwenden, damit Sie alte Kennwörter wiederverwenden können. Dies wird später in diesem Artikel erläutert.

Erstellen von Modellen und Mitgliedschaftsseiten

Wie bereits erwähnt, verwendet das Identitätsfeature Entity Framework, um standardmäßig mit der Datenbank zum Speichern von Kontoinformationen zu kommunizieren. Um mit vorhandenen Daten in der Tabelle zu arbeiten, müssen wir Modellklassen erstellen, die den Tabellen wieder zugeordnet sind, und sie in das Identitätssystem einbinden. Im Rahmen des Identitätsvertrags sollten die Modellklassen entweder die in der Identity.Core-DLL definierten Schnittstellen implementieren oder die vorhandene Implementierung dieser Schnittstellen erweitern, die in Microsoft.AspNet.Identity.EntityFramework verfügbar sind.

In unserem Beispiel enthalten die Tabellen AspNetRoles, AspNetUserClaims, AspNetLogins und AspNetUserRole Spalten, die der vorhandenen Implementierung des Identity-Systems ähneln. Daher können wir die vorhandenen Klassen wiederverwenden, um diesen Tabellen zuzuordnen. Die AspNetUser-Tabelle enthält einige zusätzliche Spalten, die zum Speichern zusätzlicher Informationen aus den SQL-Mitgliedschaftstabellen verwendet werden. Dies kann zugeordnet werden, indem Sie eine Modellklasse erstellen, die die vorhandene Implementierung von "IdentityUser" erweitert und die zusätzlichen Eigenschaften hinzufügen.

  1. Erstellen Sie im Projekt einen Ordner Models, und fügen Sie die Klasse User hinzu. Der Name der Klasse sollte mit den Daten übereinstimmen, die in der Spalte "Diskriminator" der Tabelle "AspnetUsers" hinzugefügt wurden.

    Screenshot: Erstellen eines Models-Ordners im Projekt und Hinzufügen einer Klasse User

    Die User-Klasse sollte die IdentityUser-Klasse erweitern, die in der Dll Microsoft.AspNet.Identity.EntityFramework gefunden wird. Deklarieren Sie die Eigenschaften in der Klasse, die den AspNetUser-Spalten zugeordnet sind. Die Eigenschaften ID, Benutzername, PasswordHash und SecurityStamp sind im IdentityUser definiert und werden daher nicht angegeben. Im Folgenden finden Sie den Code für die User-Klasse, die über alle Eigenschaften verfügt.

    public class User : IdentityUser
    {
        public User()
        {
            CreateDate = DateTime.Now;
            IsApproved = false;
            LastLoginDate = DateTime.Now;
            LastActivityDate = DateTime.Now;
            LastPasswordChangedDate = DateTime.Now;
            LastLockoutDate = DateTime.Parse("1/1/1754");
            FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1/1/1754");
            FailedPasswordAttemptWindowStart = DateTime.Parse("1/1/1754");
        }
    
        public System.Guid ApplicationId { get; set; }
        public string MobileAlias { get; set; }
        public bool IsAnonymous { get; set; }
        public System.DateTime LastActivityDate { get; set; }
        public string MobilePIN { get; set; }
        public string LoweredEmail { get; set; }
        public string LoweredUserName { get; set; }
        public string PasswordQuestion { get; set; }
        public string PasswordAnswer { get; set; }
        public bool IsApproved { get; set; }
        public bool IsLockedOut { get; set; }
        public System.DateTime CreateDate { get; set; }
        public System.DateTime LastLoginDate { get; set; }
        public System.DateTime LastPasswordChangedDate { get; set; }
        public System.DateTime LastLockoutDate { get; set; }
        public int FailedPasswordAttemptCount { get; set; }
        public System.DateTime FailedPasswordAttemptWindowStart { get; set; }
        public int FailedPasswordAnswerAttemptCount { get; set; }
        public System.DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
        public string Comment { get; set; }
    }
    
  2. Eine Entity Framework DbContext-Klasse ist erforderlich, um Daten in Modellen zurück in Tabellen zu speichern und Daten aus Tabellen abzurufen, um die Modelle aufzufüllen. Die Microsoft.AspNet.Identity.EntityFramework-DLL definiert die IdentityDbContext-Klasse, die mit den Identity-Tabellen interagiert, um Informationen abzurufen und zu speichern. Der IdentityDbContext-tuser<> verwendet eine "TUser"-Klasse, bei der es sich um eine beliebige Klasse handeln kann, die die IdentityUser-Klasse erweitert.

    Erstellen Sie eine neue Klasse ApplicationDBContext, die IdentityDbContext unter dem Ordner "Models" erweitert, und übergeben Sie die in Schritt 1 erstellte Klasse "User".

    public class ApplicationDbContext : IdentityDbContext<User>
    {
            
    }
    
  3. Die Benutzerverwaltung im neuen Identity-System erfolgt mithilfe der UserManager-Tuser-Klasse<>, die in der Microsoft.AspNet.Identity.EntityFramework-DLL definiert ist. Wir müssen eine benutzerdefinierte Klasse erstellen, die UserManager erweitert und die in Schritt 1 erstellte Klasse "User" übergeben.

    Erstellen Sie im Ordner Models eine neue Klasse UserManager, die den UserManager-Benutzer<erweitert.>

    public class UserManager : UserManager<User>
    {
            
    }
    
  4. Die Kennwörter der Benutzer der Anwendung werden verschlüsselt und in der Datenbank gespeichert. Der in der SQL-Mitgliedschaft verwendete Kryptoalgorithmus unterscheidet sich von dem im neuen Identitätssystem. Um alte Kennwörter wiederzuverwenden, müssen wir Kennwörter selektiv entschlüsseln, wenn sich alte Benutzer mit dem Algorithmus für SQL-Mitgliedschaften anmelden, während sie den Kryptoalgorithmus in Identität für die neuen Benutzer verwenden.

    Die UserManager-Klasse verfügt über die Eigenschaft "PasswordHasher", die eine instance einer Klasse speichert, die die "IPasswordHasher"-Schnittstelle implementiert. Dies wird verwendet, um Kennwörter während Transaktionen der Benutzerauthentifizierung zu ver-/entschlüsseln. Erstellen Sie in der in Schritt 3 definierten UserManager-Klasse eine neue Klasse SQLPasswordHasher, und kopieren Sie den folgenden Code.

    public class SQLPasswordHasher : PasswordHasher
    {
        public override string HashPassword(string password)
        {
            return base.HashPassword(password);
        }
    
        public override PasswordVerificationResult VerifyHashedPassword(string  hashedPassword, string providedPassword)
        {
            string[] passwordProperties = hashedPassword.Split('|');
            if (passwordProperties.Length != 3)
            {
                return base.VerifyHashedPassword(hashedPassword, providedPassword);
            }
            else
            {
                string passwordHash = passwordProperties[0];
                int passwordformat = 1;
                string salt = passwordProperties[2];
                if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
                {
                    return PasswordVerificationResult.SuccessRehashNeeded;
                }
                else
                {
                    return PasswordVerificationResult.Failed;
                }
            }
        }
    
        //This is copied from the existing SQL providers and is provided only for back-compat.
        private string EncryptPassword(string pass, int passwordFormat, string salt)
        {
            if (passwordFormat == 0) // MembershipPasswordFormat.Clear
                return pass;
    
            byte[] bIn = Encoding.Unicode.GetBytes(pass);
            byte[] bSalt = Convert.FromBase64String(salt);
            byte[] bRet = null;
    
            if (passwordFormat == 1)
            { // MembershipPasswordFormat.Hashed 
                HashAlgorithm hm = HashAlgorithm.Create("SHA1");
                if (hm is KeyedHashAlgorithm)
                {
                    KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
                    if (kha.Key.Length == bSalt.Length)
                    {
                        kha.Key = bSalt;
                    }
                    else if (kha.Key.Length < bSalt.Length)
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
                        kha.Key = bKey;
                    }
                    else
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        for (int iter = 0; iter < bKey.Length; )
                        {
                            int len = Math.Min(bSalt.Length, bKey.Length - iter);
                            Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
                            iter += len;
                        }
                        kha.Key = bKey;
                    }
                    bRet = kha.ComputeHash(bIn);
                }
                else
                {
                    byte[] bAll = new byte[bSalt.Length + bIn.Length];
                    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
                    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
                    bRet = hm.ComputeHash(bAll);
                }
            }
    
            return Convert.ToBase64String(bRet);
        }
    

    Beheben Sie die Kompilierungsfehler, indem Sie die Namespaces System.Text und System.Security.Cryptography importieren.

    Die EncodePassword-Methode verschlüsselt das Kennwort gemäß der standardmäßigen SQL-Mitgliedschafts-Kryptoimplementierung. Dies wird aus der System.Web-DLL übernommen. Wenn die alte App eine benutzerdefinierte Implementierung verwendet hat, sollte dies hier widergespiegelt werden. Wir müssen zwei weitere Methoden HashPassword und VerifyHashedPassword definieren, die die EncodePassword-Methode verwenden, um ein bestimmtes Kennwort zu hashen oder ein Nur-Text-Kennwort mit dem in der Datenbank vorhandenen kennwort zu überprüfen.

    Das SQL-Mitgliedschaftssystem hat PasswordHash, PasswordSalt und PasswordFormat verwendet, um das kennwort zu hashen, das von Benutzern eingegeben wurde, wenn sie sich registrieren oder ihr Kennwort ändern. Während der Migration werden alle drei Felder in der Spalte PasswordHash in der Tabelle AspNetUser durch das Zeichen "|" getrennt gespeichert. Wenn sich ein Benutzer anmeldet und das Kennwort über diese Felder verfügt, verwenden wir die SQL-Mitgliedschafts-Krypto, um das Kennwort zu überprüfen. Andernfalls verwenden wir die Standardkryptografie des Identitätssystems, um das Kennwort zu überprüfen. Auf diese Weise müssen alte Benutzer ihre Kennwörter nach der Migration der App nicht ändern.

  5. Deklarieren Sie den Konstruktor für die UserManager-Klasse, und übergeben Sie ihn als SQLPasswordHasher an die -Eigenschaft im Konstruktor.

    public UserManager()
                : base(new UserStore<User>(new ApplicationDbContext()))
    {
                this.PasswordHasher = new SQLPasswordHasher();
    }
    

Erstellen neuer Kontoverwaltungsseiten

Der nächste Schritt bei der Migration besteht darin, Kontoverwaltungsseiten hinzuzufügen, die es einem Benutzer ermöglichen, sich zu registrieren und sich anzumelden. Die alten Kontoseiten der SQL-Mitgliedschaft verwenden Steuerelemente, die nicht mit dem neuen Identitätssystem funktionieren. Um die neuen Benutzerverwaltungsseiten hinzuzufügen, folgen Sie dem Tutorial unter diesem Linkhttps://www.asp.net/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project, beginnend mit dem Schritt "Hinzufügen von Web Forms zum Registrieren von Benutzern zu Ihrer Anwendung", da wir das Projekt bereits erstellt und die NuGet-Pakete hinzugefügt haben.

Wir müssen einige Änderungen vornehmen, damit das Beispiel mit dem projekt arbeiten kann, das wir hier haben.

  • Die Codebehindklassen Register.aspx.cs und Login.aspx.cs verwenden die UserManager aus den Identity-Paketen, um einen Benutzer zu erstellen. Verwenden Sie für dieses Beispiel den UserManager, der im Ordner Models hinzugefügt wurde, indem Sie die oben genannten Schritte ausführen.

  • Verwenden Sie die erstellte User-Klasse anstelle der IdentityUser-Codebehind-Klassen Register.aspx.cs und Login.aspx.cs. Dadurch wird unsere benutzerdefinierte Benutzerklasse in das Identitätssystem eingebunden.

  • Der Teil zum Erstellen der Datenbank kann übersprungen werden.

  • Der Entwickler muss die ApplicationId festlegen, damit der neue Benutzer mit der aktuellen Anwendungs-ID übereinstimmt. Dazu können Sie die ApplicationId für diese Anwendung abfragen, bevor ein Benutzerobjekt in der Register.aspx.cs-Klasse erstellt wird, und es vor dem Erstellen eines Benutzers festlegen.

    Beispiel:

    Definieren Sie eine Methode auf der Seite "Register.aspx.cs", um die aspnet_Applications Tabelle abzufragen und die Anwendungs-ID entsprechend dem Anwendungsnamen abzurufen.

    private Guid GetApplicationID()
    {
        using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
        {
            string queryString = "SELECT ApplicationId from aspnet_Applications WHERE ApplicationName = '/'"; //Set application name as in database
    
            SqlCommand command = new SqlCommand(queryString, connection);
            command.Connection.Open();
    
            var reader = command.ExecuteReader();
            while (reader.Read())
            {
                return reader.GetGuid(0);
            }
    
            return Guid.NewGuid();
        }
    }
    

    Rufen Sie nun dies für das Benutzerobjekt ab.

    var currentApplicationId = GetApplicationID();
    
    User user = new User() { UserName = Username.Text,
    ApplicationId=currentApplicationId, …};
    

Verwenden Sie den alten Benutzernamen und das alte Kennwort, um einen vorhandenen Benutzer anzumelden. Verwenden Sie die Seite Registrieren, um einen neuen Benutzer zu erstellen. Stellen Sie außerdem sicher, dass die Benutzer wie erwartet in Rollen sind.

Das Portieren auf das Identitätssystem hilft dem Benutzer, der Anwendung Open Authentication (OAuth) hinzuzufügen. Sehen Sie sich das Beispiel hier an, in dem OAuth aktiviert ist.

Nächste Schritte

In diesem Tutorial haben wir gezeigt, wie Benutzer von der SQL-Mitgliedschaft in ASP.NET Identity portieren, aber keine Profildaten portieren. Im nächsten Tutorial untersuchen wir das Portieren von Profildaten aus der SQL-Mitgliedschaft in das neue Identitätssystem.

Sie können am Ende dieses Artikels Feedback hinterlassen.

Vielen Dank an Tom Dykstra und Rick Anderson für die Durchsicht des Artikels.