Migrowanie istniejącej witryny internetowej z członkostwa SQL do systemu ASP.NET Identity

Autor : Rick Anderson, Suhas Joshi

W tym samouczku przedstawiono kroki migracji istniejącej aplikacji internetowej z danymi użytkownika i roli utworzonymi przy użyciu członkostwa SQL w nowym systemie tożsamości ASP.NET. Takie podejście polega na zmianie istniejącego schematu bazy danych na wymagany przez ASP.NET Identity i przypinanie do niego starych/nowych klas. Po przyjęciu tego podejścia po przeprowadzeniu migracji bazy danych przyszłe aktualizacje tożsamości będą obsługiwane bez wysiłku.

Na potrzeby tego samouczka utworzymy szablon aplikacji internetowej (Web Forms) utworzony przy użyciu programu Visual Studio 2010 w celu utworzenia danych użytkownika i roli. Następnie użyjemy skryptów SQL, aby zmigrować istniejącą bazę danych do tabel wymaganych przez system tożsamości. Następnie zainstalujemy niezbędne pakiety NuGet i dodamy nowe strony zarządzania kontami, które używają systemu tożsamości do zarządzania członkostwem. Podczas testowania migracji użytkownicy utworzeni przy użyciu członkostwa SQL powinni mieć możliwość logowania, a nowi użytkownicy powinni mieć możliwość rejestrowania. Pełny przykład można znaleźć tutaj. Zobacz również Migrowanie z członkostwa ASP.NET do tożsamości ASP.NET.

Wprowadzenie

Tworzenie aplikacji z członkostwem SQL

  1. Musimy zacząć od istniejącej aplikacji, która używa członkostwa SQL i ma dane użytkownika i roli. Na potrzeby tego artykułu utworzymy aplikację internetową w programie Visual Studio 2010.

    Zrzut ekranu przedstawiający tworzenie aplikacji internetowej w programie Visual Studio 2010.

  2. Za pomocą narzędzia ASP.NET Configuration utwórz 2 użytkowników: oldAdminUser i oldUser.

    Zrzut ekranu przedstawiający element A S P . N E T Configuration tool to create two users(N E T Configuration tool to create two users).

    Zrzut ekranu przedstawiający narzędzie administracyjne witryny internetowej do zarządzania wszystkimi zabezpieczeniami.

  3. Utwórz rolę o nazwie Administracja i dodaj rolę "oldAdminUser" jako użytkownik w tej roli.

    Zrzut ekranu przedstawiający rolę Administracja utworzoną jako użytkownik w tej roli.

  4. Utwórz sekcję Administracja witryny przy użyciu pliku Default.aspx. Ustaw tag autoryzacji w pliku web.config, aby umożliwić dostęp tylko do użytkowników w rolach Administracja. Więcej informacji można znaleźć tutaj https://www.asp.net/web-forms/tutorials/security/roles/role-based-authorization-cs

    Zrzut ekranu przedstawiający sekcję Administracja witryny.

  5. Wyświetl bazę danych w Eksploratorze serwera, aby poznać tabele utworzone przez system członkostwa SQL. Dane logowania użytkownika są przechowywane w tabelach aspnet_Users i aspnet_Membership, podczas gdy dane roli są przechowywane w tabeli aspnet_Roles. Informacje o tym, którzy użytkownicy są przechowywani w tabeli aspnet_UsersInRoles. W przypadku podstawowego zarządzania członkostwem wystarczy przekierować informacje z powyższych tabel do systemu ASP.NET Identity.

    Zrzut ekranu przedstawiający Eksploratora serwera, aby wyświetlić bazę danych w celu zrozumienia tabel utworzonych przez system członkostwa w języku S Q L.

Migrowanie do Visual Studio 2013

  1. Zainstaluj Visual Studio Express 2013 dla sieci Web lub Visual Studio 2013 wraz z najnowszymi aktualizacjami.

  2. Otwórz powyższy projekt w zainstalowanej wersji programu Visual Studio. Jeśli SQL Server Express nie jest zainstalowana na maszynie, po otwarciu projektu zostanie wyświetlony monit, ponieważ parametry połączenia używają programu SQL Express. Możesz wybrać instalację programu SQL Express lub w miarę obejścia zmiany parametrów połączenia na LocalDb. W tym artykule zmienimy go na LocalDb.

  3. Otwórz web.config i zmień parametry połączenia z . SQLExpress do (LocalDb)v11.0. Usuń element "User Instance=true" z parametrów połączenia.

    Zrzut ekranu przedstawiający zmianę parametrów połączenia w celu przeprowadzenia migracji do Visual Studio 2013.

  4. Otwórz Eksploratora serwera i sprawdź, czy można zaobserwować schemat i dane tabeli.

  5. System ASP.NET Identity współdziała z wersją 4.5 lub nowszą platformy. Przekieruj aplikację do wersji 4.5 lub nowszej.

    Zrzut ekranu przedstawiający element A S P . N E T Identity system.

    Skompiluj projekt, aby sprawdzić, czy nie ma żadnych błędów.

Instalowanie pakietów Nuget

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt Zarządzaj pakietami> NuGet. W polu wyszukiwania wprowadź "Asp.net Tożsamość". Wybierz pakiet na liście wyników, a następnie kliknij przycisk Zainstaluj. Zaakceptuj umowę licencyjną, klikając przycisk "Akceptuję". Należy pamiętać, że ten pakiet zainstaluje pakiety zależności: EntityFramework i Microsoft ASP.NET Identity Core. Podobnie zainstaluj następujące pakiety (pomiń ostatnie 4 pakiety OWIN, jeśli nie chcesz włączyć logowania OAuth):

    • Microsoft.AspNet.Identity.Owin

    • Microsoft.Owin.Host.SystemWeb

    • Microsoft.Owin.Security.Facebook

    • Microsoft.Owin.Security.Google

    • Microsoft.Owin.Security.MicrosoftAccount

    • Microsoft.Owin.Security.Twitter

      Zrzut ekranu przedstawiający instalowanie pakietów Nuget w Eksplorator rozwiązań.

Migrowanie bazy danych do nowego systemu tożsamości

Następnym krokiem jest migracja istniejącej bazy danych do schematu wymaganego przez system ASP.NET Identity. Aby to osiągnąć, uruchomimy skrypt SQL zawierający zestaw poleceń w celu utworzenia nowych tabel i zmigrowania istniejących informacji o użytkowniku do nowych tabel. Plik skryptu można znaleźć tutaj.

Ten plik skryptu jest specyficzny dla tego przykładu. Jeśli schemat tabel utworzonych przy użyciu członkostwa SQL został dostosowany lub zmodyfikowany, należy odpowiednio zmienić skrypty.

Jak wygenerować skrypt SQL na potrzeby migracji schematu

Aby klasy ASP.NET Identity działały bez użycia danych istniejących użytkowników, musimy przeprowadzić migrację schematu bazy danych do klasy wymaganej przez usługę ASP.NET Identity. Możemy to zrobić, dodając nowe tabele i kopiując istniejące informacje do tych tabel. Domyślnie ASP.NET Identity używa elementu EntityFramework do mapowania klas modelu tożsamości z powrotem do bazy danych w celu przechowywania/pobierania informacji. Te klasy modeli implementują podstawowe interfejsy tożsamości definiujące obiekty użytkownika i roli. Tabele i kolumny w bazie danych są oparte na tych klasach modelu. Klasy modelu EntityFramework w usłudze Identity w wersji 2.1.0 i ich właściwości są zdefiniowane poniżej

IdentityUser Typ IdentityRole IdentityUserRole IdentityUserLogin IdentityUserClaim
Id ciąg Id Identyfikator roli Klucz dostawcy Id
Nazwa użytkownika ciąg Nazwa UserId UserId Claimtype
PasswordHash ciąg LoginProvider ClaimValue
SecurityStamp ciąg User_id
E-mail ciąg
EmailConfirmed bool
PhoneNumber ciąg
PhoneNumberConfirmed bool
LockoutEnabled bool
LockoutEndDate DateTime
AccessFailedCount int

Musimy mieć tabele dla każdego z tych modeli z kolumnami odpowiadającymi właściwościom. Mapowanie między klasami i tabelami jest definiowane w OnModelCreating metodzie IdentityDBContext. Jest to znane jako płynna metoda konfiguracji interfejsu API i więcej informacji można znaleźć tutaj. Konfiguracja klas jest jak wspomniano poniżej

Klasa Tabela Klucz podstawowy Klucz obcy
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>

Dzięki tym informacjom możemy utworzyć instrukcje SQL w celu utworzenia nowych tabel. Możemy napisać każdą instrukcję indywidualnie lub wygenerować cały skrypt przy użyciu poleceń programu PowerShell EntityFramework, które następnie możemy edytować zgodnie z potrzebami. W tym celu w programie VS otwórz konsolę Menedżera pakietów z menu Widok lub Narzędzia

  • Uruchom polecenie "Enable-Migrations", aby włączyć migracje entityFramework.
  • Uruchom polecenie "Add-migration initial", które tworzy początkowy kod konfiguracji w celu utworzenia bazy danych w języku C#/VB.
  • Ostatnim krokiem jest uruchomienie polecenia "Update-Database –Script", które generuje skrypt SQL na podstawie klas modelu.

Niektóre polecenia nie są obsługiwane, jeśli aplikacja używa biblioteki SQLite jako magazynu danych identity. Ze względu na ograniczenia aparatu bazy danych Alter polecenia zgłaszają następujący wyjątek:

"System.NotSupportedException: SQLite nie obsługuje tej operacji migracji".

W miarę pracy uruchom migracje Code First w bazie danych, aby zmienić tabele.

Ten skrypt generowania bazy danych może służyć jako początek, w którym wprowadzimy dodatkowe zmiany w celu dodania nowych kolumn i skopiowania danych. Zaletą tej funkcji jest wygenerowanie _MigrationHistory tabeli, która jest używana przez element EntityFramework do modyfikowania schematu bazy danych, gdy klasy modeli zmieniają się w przyszłych wersjach wersji usługi Identity.

Informacje o użytkowniku członkostwa SQL miały inne właściwości oprócz tych w klasie Modelu użytkownika Tożsamości, czyli e-mail, próbach hasła, dacie ostatniego logowania, ostatniej dacie blokady itp. Jest to przydatne informacje i chcemy, aby były przenoszone do systemu tożsamości. Można to zrobić, dodając dodatkowe właściwości do modelu użytkownika i mapując je z powrotem do kolumn tabeli w bazie danych. Możemy to zrobić, dodając klasę, która podklasuje IdentityUser model. Możemy dodać właściwości do tej klasy niestandardowej i edytować skrypt SQL, aby dodać odpowiednie kolumny podczas tworzenia tabeli. Kod dla tej klasy został opisany w dalszej części artykułu. Skrypt SQL do tworzenia AspnetUsers tabeli po dodaniu nowych właściwości będzie

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]),
);

Następnie musimy skopiować istniejące informacje z bazy danych członkostwa SQL do nowo dodanych tabel dla usługi Identity. Można to zrobić za pomocą języka SQL, kopiując dane bezpośrednio z jednej tabeli do innej. Aby dodać dane do wierszy tabeli, użyjemy INSERT INTO [Table] konstrukcji. Aby skopiować z innej tabeli, możemy użyć INSERT INTO instrukcji wraz z instrukcją SELECT . Aby uzyskać wszystkie informacje o użytkowniku, musimy wykonać zapytanie dotyczące tabel aspnet_Users i aspnet_Membership oraz skopiować dane do tabeli AspNetUsers . Używamy instrukcji INSERT INTOJOIN i SELECT i.LEFT OUTER JOIN Aby uzyskać więcej informacji na temat wykonywania zapytań i kopiowania danych między tabelami, zapoznaj się z tym linkiem. Ponadto tabele AspnetUserLogins i AspnetUserClaims są puste, ponieważ domyślnie nie ma żadnych informacji w członkostwie SQL, które są mapowane na tę wartość. Jedyne skopiowane informacje są przeznaczone dla użytkowników i ról. W przypadku projektu utworzonego w poprzednich krokach zapytanie SQL w celu skopiowania informacji do tabeli users będzie

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;

W powyższej instrukcji SQL informacje o każdym użytkowniku z tabel aspnet_Users i aspnet_Membership są kopiowane do kolumn tabeli AspnetUsers . Jedyną modyfikacją wykonaną w tym miejscu jest skopiowanie hasła. Ponieważ algorytm szyfrowania haseł w członkostwie SQL używał wartości "PasswordSalt" i "PasswordFormat", skopiujemy to również wraz z hasłem skrótu, aby można było go użyć do odszyfrowania hasła według tożsamości. Wyjaśniono to w dalszej części artykułu podczas podłączania hasła niestandardowego.

Ten plik skryptu jest specyficzny dla tego przykładu. W przypadku aplikacji, które mają dodatkowe tabele, deweloperzy mogą stosować podobne podejście do dodawania dodatkowych właściwości w klasie modelu użytkownika i mapować je na kolumny w tabeli AspnetUsers. Aby uruchomić skrypt,

  1. Otwórz Eksploratora serwera. Rozwiń połączenie "ApplicationServices", aby wyświetlić tabele. Kliknij prawym przyciskiem myszy węzeł Tabele i wybierz opcję "Nowe zapytanie"

    Zrzut ekranu przedstawiający Eksploratora programu Open Server w celu rozwinięcia połączenia usług aplikacji w celu wyświetlenia tabel.

  2. W oknie zapytania skopiuj i wklej cały skrypt SQL z pliku Migrations.sql. Uruchom plik skryptu, naciskając przycisk strzałki "Wykonaj".

    Zrzut ekranu przedstawiający okno zapytania, aby skopiować i wkleić cały skrypt S Q L.

    Odśwież okno Eksplorator serwera. W bazie danych są tworzone pięć nowych tabel.

    Zrzut ekranu przedstawiający odświeżanie okna Eksploratora serwera w celu utworzenia pięciu nowych tabel w bazie danych.

    Zrzut ekranu przedstawiający tabele w połączeniach danych.

    Poniżej przedstawiono sposób mapowania informacji w tabelach członkostwa SQL do nowego systemu tożsamości.

    aspnet_Roles —> AspNetRoles

    asp_netUsers i asp_netMembership —> AspNetUsers

    aspnet_UserInRoles —> AspNetUserRoles

    Jak wyjaśniono w powyższej sekcji, tabele AspNetUserClaims i AspNetUserLogins są puste. Pole "Dyskryminujące" w tabeli AspNetUser powinno być zgodne z nazwą klasy modelu, która jest zdefiniowana jako następny krok. Ponadto kolumna PasswordHash znajduje się w postaci "zaszyfrowane hasło |password salt|password format". Dzięki temu można używać specjalnej logiki kryptograficznej członkostwa SQL, aby można było ponownie używać starych haseł. Wyjaśniono to w dalszej części artykułu.

Tworzenie modeli i stron członkostwa

Jak wspomniano wcześniej, funkcja Identity używa programu Entity Framework do komunikacji z bazą danych do przechowywania informacji o koncie domyślnie. Aby pracować z istniejącymi danymi w tabeli, musimy utworzyć klasy modelu, które są mapowania z powrotem do tabel i podłączać je do systemu Tożsamości. W ramach kontraktu tożsamości klasy modelu powinny implementować interfejsy zdefiniowane w bibliotece DLL Identity.Core lub rozszerzyć istniejącą implementację tych interfejsów dostępnych w elemecie Microsoft.AspNet.Identity.EntityFramework.

W naszym przykładzie tabele AspNetRoles, AspNetUserClaims, AspNetLogins i AspNetUserRole zawierają kolumny podobne do istniejącej implementacji systemu Identity. W związku z tym możemy ponownie użyć istniejących klas do mapowania na te tabele. Tabela AspNetUser zawiera dodatkowe kolumny, które są używane do przechowywania dodatkowych informacji z tabel członkostwa SQL. Można to zamapować, tworząc klasę modelu, która rozszerza istniejącą implementację klasy "IdentityUser" i dodaje dodatkowe właściwości.

  1. Utwórz folder Models w projekcie i dodaj klasę User. Nazwa klasy powinna być zgodna z danymi dodanymi w kolumnie "Dyskryminator" tabeli "AspnetUsers".

    Zrzut ekranu przedstawiający tworzenie folderu Models w projekcie i dodawanie klasy Użytkownik.

    Klasa User powinna rozszerzyć klasę IdentityUser znajdującą się w bibliotece dll Microsoft.AspNet.Identity.EntityFramework . Zadeklaruj właściwości w klasie mapujące z powrotem na kolumny AspNetUser. Właściwości ID, Username, PasswordHash i SecurityStamp są zdefiniowane w identityUser i dlatego są pomijane. Poniżej znajduje się kod klasy User, który ma wszystkie właściwości

    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. Klasa DbContext programu Entity Framework jest wymagana do utrwalania danych w modelach z powrotem do tabel i pobierania danych z tabel w celu wypełnienia modeli. Biblioteka DLL Microsoft.AspNet.Identity.EntityFramework definiuje klasę IdentityDbContext, która współdziała z tabelami Identity w celu pobierania i przechowywania informacji. Element tuser> IdentityDbContext<przyjmuje klasę "TUser", która może być dowolną klasą rozszerzającą klasę IdentityUser.

    Utwórz nową klasę ApplicationDBContext, która rozszerza element IdentityDbContext w folderze "Models", przekazując klasę "User" utworzoną w kroku 1

    public class ApplicationDbContext : IdentityDbContext<User>
    {
            
    }
    
  3. Zarządzanie użytkownikami w nowym systemie tożsamości odbywa się przy użyciu klasy tuser> UserManager<zdefiniowanej w bibliotece dll Microsoft.AspNet.Identity.EntityFramework. Musimy utworzyć klasę niestandardową, która rozszerza klasę UserManager, przekazując klasę "Użytkownik" utworzoną w kroku 1.

    W folderze Models utwórz nową klasę UserManager, która rozszerza użytkownika UserManager<>

    public class UserManager : UserManager<User>
    {
            
    }
    
  4. Hasła użytkowników aplikacji są szyfrowane i przechowywane w bazie danych. Algorytm kryptograficzny używany w członkostwie SQL różni się od algorytmu w nowym systemie tożsamości. Aby ponownie używać starych haseł, należy selektywnie odszyfrować hasła, gdy starzy użytkownicy logują się przy użyciu algorytmu członkostwa SQL podczas korzystania z algorytmu kryptograficznego w tożsamości dla nowych użytkowników.

    Klasa UserManager ma właściwość "PasswordHasher", która przechowuje wystąpienie klasy implementujące interfejs "IPasswordHasher". Służy to do szyfrowania/odszyfrowywania haseł podczas transakcji uwierzytelniania użytkownika. W klasie UserManager zdefiniowanej w kroku 3 utwórz nową klasę SQLPasswordHasher i skopiuj poniższy kod.

    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);
        }
    

    Rozwiąż błędy kompilacji, importując przestrzenie nazw System.Text i System.Security.Cryptography.

    Metoda EncodePassword szyfruje hasło zgodnie z domyślną implementacją kryptograficzną członkostwa SQL. Jest to pobierane z biblioteki Dll System.Web. Jeśli stara aplikacja użyła implementacji niestandardowej, powinna ona zostać odzwierciedlona w tym miejscu. Musimy zdefiniować dwie inne metody HashPassword i VerifyHashedPassword , które używają metody EncodePassword w celu utworzenia skrótu danego hasła lub zweryfikowania hasła w postaci zwykłego tekstu przy użyciu hasła istniejącego w bazie danych.

    W systemie członkostwa SQL użyto skrótu PasswordHash, PasswordSalt i PasswordFormat w celu utworzenia skrótu hasła wprowadzonego przez użytkowników podczas rejestrowania lub zmiany hasła. Podczas migracji wszystkie trzy pola są przechowywane w kolumnie PasswordHash w tabeli AspNetUser oddzielonej znakiem '|'. Gdy użytkownik loguje się i hasło ma te pola, używamy kryptografii członkostwa SQL do sprawdzania hasła; W przeciwnym razie używamy domyślnej kryptografii systemu tożsamości do zweryfikowania hasła. Dzięki temu starzy użytkownicy nie musieliby zmieniać swoich haseł po przeprowadzeniu migracji aplikacji.

  5. Zadeklaruj konstruktor dla klasy UserManager i przekaż go jako element SQLPasswordHasher do właściwości w konstruktorze.

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

Tworzenie nowych stron zarządzania kontami

Następnym krokiem migracji jest dodanie stron zarządzania kontami, które umożliwią użytkownikowi zarejestrowanie się i zalogowanie. Stare strony konta z członkostwa SQL używają kontrolek, które nie działają z nowym systemem tożsamości. Aby dodać nowe strony zarządzania użytkownikami, wykonaj czynności opisane w samouczku pod tym linkiemhttps://www.asp.net/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project, zaczynając od kroku "Dodawanie Web Forms do rejestrowania użytkowników w aplikacji", ponieważ projekt został już utworzony i dodano pakiety NuGet.

Musimy wprowadzić pewne zmiany w przykładzie, aby pracować z projektem, który mamy tutaj.

  • Kod Register.aspx.cs i Login.aspx.cs za klasami korzysta z UserManager pakietów Identity, aby utworzyć użytkownika. W tym przykładzie użyj menedżera użytkowników dodanego w folderze Models, wykonując kroki wymienione wcześniej.

  • Użyj klasy User utworzonej zamiast klasy IdentityUser w plikach Register.aspx.cs i Login.aspx.cs za klasami. Spowoduje to wpięcie w naszej niestandardowej klasie użytkownika do systemu tożsamości.

  • Część do utworzenia bazy danych można pominąć.

  • Deweloper musi ustawić identyfikator ApplicationId dla nowego użytkownika, aby był zgodny z bieżącym identyfikatorem aplikacji. Można to zrobić, wykonując zapytanie dotyczące identyfikatora ApplicationId dla tej aplikacji, zanim obiekt użytkownika zostanie utworzony w klasie Register.aspx.cs i ustawić go przed utworzeniem użytkownika.

    Przykład:

    Zdefiniuj metodę na stronie Register.aspx.cs, aby wysłać zapytanie do tabeli aspnet_Applications i pobrać identyfikator aplikacji zgodnie z nazwą aplikacji

    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();
        }
    }
    

    Teraz ustaw tę wartość w obiekcie użytkownika

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

Użyj starej nazwy użytkownika i hasła, aby zalogować istniejącego użytkownika. Użyj strony Rejestrowanie, aby utworzyć nowego użytkownika. Sprawdź również, czy użytkownicy są w rolach zgodnie z oczekiwaniami.

Przenoszenie do systemu tożsamości pomaga użytkownikowi dodać do aplikacji protokół Open Authentication (OAuth). Zapoznaj się z przykładem w tym miejscu , w którym włączono uwierzytelnianie OAuth.

Następne kroki

W tym samouczku pokazano, jak przenosić użytkowników z członkostwa SQL w usłudze ASP.NET Identity, ale nie portowaliśmy danych profilu. W następnym samouczku omówimy przenoszenie danych profilu z członkostwa SQL do nowego systemu tożsamości.

Możesz zostawić opinię w dolnej części tego artykułu.

Dzięki Tomowi Dykstra i Rickowi Andersonowi do przejrzenia artykułu.