Migrieren von ASP.NET-Mitgliedschaftsauthentifizierung zu ASP.NET Core 2.0 Identity

Von Isaac Levin

In diesem Artikel wird die Migration des Datenbankschemas für ASP.NET-Apps mithilfe von Mitgliedschaftsauthentifizierung zu ASP.NET Core 2.0 Identity gezeigt.

Hinweis

Dieses Dokument beschreibt die erforderlichen Schritte zum Migrieren des Datenbankschemas für mitgliedschaftsbasierte ASP.NET-Apps zum Datenbankschema, das für ASP.NET Core Identity verwendet wird. Weitere Informationen zum Migrieren von mitgliedschaftsbasierter ASP.NET-Authentifizierung zu ASP.NET Identity finden Sie unter Migrieren einer vorhandenen App von SQL-Mitgliedschaft zu ASP.NET Identity. Weitere Informationen zu ASP.NET Core Identity finden Sie unter Einführung in Identity in ASP.NET Core.

Überprüfen des Mitgliedschaftsschemas

Vor ASP.NET 2.0 wurden Entwickler damit beauftragt, den gesamten Authentifizierungs- und Autorisierungsprozess für ihre Apps zu erstellen. Mit ASP.NET 2.0 wurde Mitgliedschaft eingeführt. Sie bietet eine Bausteinlösung für den Umgang mit Sicherheit in ASP.NET-Apps. Entwickler konnten nun Bootstrapping für ein Schema mit dem SQL Server-Registrierungstool von ASP.NET (Aspnet_regsql.exe) in eine SQL Server-Datenbank ausführen (wird nicht mehr unterstützt). Nach dem Ausführen dieses Befehls wurden die folgenden Tabellen in der Datenbank erstellt.

Membership Tables

Um vorhandene Apps zu ASP.NET Core 2.0 Identity zu migrieren, müssen die Daten in diesen Tabellen zu den Tabellen migriert werden, die vom neuen Identity-Schema verwendet werden.

ASP.NET Core Identity 2.0-Schema

ASP.NET Core 2.0 folgt dem in ASP.NET 4.5 eingeführten Identity-Prinzip. Obwohl das Prinzip gleich ist, ist die Implementierung zwischen den Frameworks unterschiedlich, sogar zwischen den Versionen von ASP.NET Core (siehe Migrieren von Authentifizierung und Identity zu ASP.NET Core 2.0).

Die schnellste Möglichkeit zum Anzeigen des Schemas für ASP.NET Core 2.0 Identity besteht darin, eine neue ASP.NET Core 2.0-App zu erstellen. Führen Sie in Visual Studio 2017 die folgenden Schritte aus:

  1. Wählen Sie Datei>Neu>Projekt aus.

  2. Erstellen Sie ein neues ASP.NET Core-Webanwendungsprojekt mit dem Namen CoreIdentitySample.

  3. Wählen Sie in der Dropdownliste ASP.NET Core 2.0 und anschließend Webanwendung aus. Diese Vorlage generiert eine Razor Pages-App. Klicken Sie vor dem Klicken auf OK auf Authentifizierung ändern.

  4. Wählen Sie einzelne Benutzerkonten für die Identity-Vorlagen aus. Klicken Sie abschließend auf OK und dann nochmals auf OK. Visual Studio erstellt ein Projekt mithilfe der ASP.NET Core Identity-Vorlage.

  5. Wählen Sie Extras>NuGet-Paket-Manager>Paket-Manager-Konsole aus, um das Fenster Paket-Manager-Konsole (PMC) zu öffnen.

  6. Navigieren Sie in PMC zum Projektstamm, und führen Sie den Befehl Entity Framework (EF) CoreUpdate-Database aus.

    ASP.NET Core 2.0 Identity verwendet EF Core, um mit der Datenbank zu interagieren, die die Authentifizierungsdaten speichert. Damit die neu erstellte App funktioniert, muss eine Datenbank zum Speichern dieser Daten vorhanden sein. Nach dem Erstellen einer neuen App besteht die schnellste Möglichkeit zum Überprüfen des Schemas in einer Datenbankumgebung darin, die Datenbank mithilfe von EF Core-Migrationen zu erstellen. Dieser Prozess erstellt eine Datenbank, entweder lokal oder an anderer Stelle, die dieses Schema imitiert. Weitere Informationen finden Sie in der oben genannten Dokumentation.

    EF Core-Befehlen verwenden die Verbindungszeichenfolge für die in appsettings.json angegebene Datenbank. Die folgende Verbindungszeichenfolge zielt auf eine Datenbank mit dem Namen asp-net-core-identity auf localhost ab. In dieser Einstellung ist EF Core für die Verwendung der DefaultConnection-Verbindungszeichenfolge konfiguriert.

    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=localhost;Database=aspnet-core-identity;Trusted_Connection=True;MultipleActiveResultSets=true"
      }
    }
    
  7. Wählen Sie Ansicht>SQL Server-Objekt-Explorer aus. Erweitern Sie den Knoten, der dem Datenbanknamen entspricht, der in der ConnectionStrings:DefaultConnection-Eigenschaft von appsettings.json angegeben ist.

    Mit dem Befehl Update-Database wurde die Datenbank erstellt, die mit dem Schema und allen Daten angegeben wurde, die für die App-Initialisierung erforderlich sind. Die folgende Abbildung zeigt die Tabellenstruktur, die mit den oben beschriebenen Schritten erstellt wurde.

    Identity Tables

Migrieren des Schemas

Es gibt geringfügige Unterschiede in den Tabellenstrukturen und Feldern für Mitgliedschaft und ASP.NET Core Identity. Das Muster für Authentifizierung/Autorisierung mit ASP.NET- und ASP.NET Core-Apps hat sich erheblich geändert. Die wichtigsten Objekte, die weiterhin mit Identity verwendet werden, sind Benutzer und Rollen. Im Folgenden finden Sie Zuordnungstabellen für Benutzer, Rollen und UserRoles.

Benutzer

Identity
(dbo.AspNetUsers)-Spalte
Typ Membership
(dbo.aspnet_Users / dbo.aspnet_Membership)-Spalte
Typ
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 wird nicht LockoutEnabled zugeordnet. IsLockedOut wird festgelegt, wenn ein Benutzer zu viele fehlgeschlagene Anmeldungen hatte und für eine festgelegte Zeitspanne gesperrt ist. LockoutEnabled ermöglicht das Sperren eines Benutzers mit zu vielen fehlgeschlagenen Anmeldeversuchen. Wenn der Benutzer zu viele fehlgeschlagene Anmeldeversuche hat, wird LockoutEnd auf ein Datum in der Zukunft festgelegt, und der Benutzer kann sich bis zu diesem Datum nicht mehr anmelden. Wenn LockoutEnabled FALSE ist, wird ein Benutzer nie bei zu vielen fehlgeschlagenen Anmeldeversuchen gesperrt. Nach OWASPist vorübergehende Kontosperrung nach mehreren Fehlversuchen ein zu einfaches Ziel für DoS-Angriffe auf legitime Benutzer.

Weitere Informationen zu Sperren finden Sie unter OWASP-Tests für schwache Sperrmechanismen.

Apps, die zu Identity migrieren, die eine Sperre bei fehlgeschlagenen Anmeldungen aktivieren möchten, sollten LockoutEnabled im Rahmen der Migration auf TRUE festlegen.

Hinweis

Nicht alle Feldzuordnungen ähneln 1:1-Beziehungen von Mitgliedschaft und ASP.NET Core Identity. In der Tabelle oben wird das standardmäßige Mitgliedschaftsbenutzerschema dem ASP.NET Core Identity-Schema zugeordnet. Alle anderen benutzerdefinierten Felder, die für die Mitgliedschaft verwendet wurden, müssen manuell zugeordnet werden. In dieser Zuordnung gibt es keine Zuordnung für Kennwörter, weil sowohl Kennwortkriterien als auch Kennwortsalts nicht zwischen den beiden migriert werden. Es wird empfohlen, das Kennwort als „null“ zu belassen und Benutzer aufzufordern, ihre Kennwörter zurückzusetzen. In ASP.NET Core Identity sollte LockoutEnd auf ein Datum in der Zukunft festgelegt werden, wenn der Benutzer gesperrt ist. Dies wird im Migrationsskript gezeigt.

Rollen

Identity
(dbo.AspNetRoles)-Spalte
Typ Membership
(dbo.aspnet_Roles)-Spalte
Typ
Id string RoleId string
Name string RoleName string
NormalizedName string LoweredRoleName string

Benutzerrollen

Identity
(dbo.AspNetUserRoles)-Spalte
Typ Membership
(dbo.aspnet_UsersInRoles)-Spalte
Typ
RoleId string RoleId string
UserId string UserId string

Beziehen Sie sich beim Erstellen eines Migrationsskripts für Benutzer und Rollen auf die oben genannten Zuordnungstabellen. Im folgenden Beispiel wird davon ausgegangen, dass Sie über zwei Datenbanken auf einem Datenbankserver verfügen. Eine Datenbank enthält das vorhandene ASP.NET-Mitgliedschaftsschema und die Daten. Die andere CoreIdentitySample-Datenbank wurde mithilfe der zuvor beschriebenen Schritte erstellt. Kommentare sind inline enthalten, um weitere Details bereitzustellen.

-- 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

Nach Abschluss des oben gezeigten Skripts wird die zuvor erstellte ASP.NET CoreIdentity-App mit den Daten von Mitgliedschaftsbenutzern aufgefüllt. Benutzer müssen ihre Kennwörter ändern, bevor sie sich anmelden.

Hinweis

Wenn das Mitgliedschaftssystem über Benutzer mit Benutzernamen verfügt, die nicht mit ihrer E-Mail-Adresse übereinstimmen, sind Änderungen an der zuvor erstellten App erforderlich, um dies zu berücksichtigen. Die Standardvorlage erwartet, dass UserName und Email identisch sind. In Situationen, in denen diese Angaben unterschiedlich sind, muss der Anmeldeprozess geändert werden, um UserName anstelle von Email zu verwenden.

Entfernen Sie im PageModel auf der Anmeldeseite das [EmailAddress]-Attribut aus der Email-Eigenschaft. Benennen Sie das Element in UserName um. Dies erfordert überall dort eine Änderung in der Ansicht und dem Seitenmodell, wo EmailAddress erwähnt wird. Das Ergebnis sieht wie folgt aus:

Fixed Login

Nächste Schritte

In diesem Tutorial haben Sie gelernt, wie Sie Benutzer aus SQL-Mitgliedschaft in ASP.NET Core 2.0 Identity portieren. Weitere Informationen zu ASP.NET Core Identity finden Sie unter Einführung in Identity.