Migrazione di un sito Web esistente dall'appartenenza SQL ad ASP.NET Identity

di Rick Anderson, Suhas Joshi

Questa esercitazione illustra i passaggi per eseguire la migrazione di un'applicazione Web esistente con dati utente e ruolo creati usando l'appartenenza SQL al nuovo sistema di identità ASP.NET. Questo approccio comporta la modifica dello schema del database esistente in quello necessario per l'identità ASP.NET e l'hook nelle classi precedenti/nuove. Dopo aver adottato questo approccio, una volta eseguita la migrazione del database, gli aggiornamenti futuri all'identità verranno gestiti senza problemi.

Per questa esercitazione verrà creato un modello di applicazione Web (Web Forms) usando Visual Studio 2010 per creare dati utente e ruolo. Verranno quindi usati gli script SQL per eseguire la migrazione del database esistente alle tabelle necessarie per il sistema di gestione delle identità. Successivamente verranno installati i pacchetti NuGet necessari e verranno aggiunte nuove pagine di gestione degli account che usano il sistema di gestione delle identità per la gestione delle appartenenze. Come test della migrazione, gli utenti creati con l'appartenenza a SQL devono essere in grado di accedere e i nuovi utenti devono essere in grado di registrarsi. L'esempio completo è disponibile qui. Vedere anche Migrazione dall'appartenenza ASP.NET all'identità di ASP.NET.

Introduzione

Creazione di un'applicazione con appartenenza SQL

  1. È necessario iniziare con un'applicazione esistente che usa l'appartenenza a SQL e dispone di dati utente e ruolo. Ai fini di questo articolo, verrà creata un'applicazione Web in Visual Studio 2010.

    Screenshot della creazione di un'applicazione Web in Visual Studio 2010.

  2. Usando lo strumento di configurazione ASP.NET, creare 2 utenti: oldAdminUser e oldUser.

    Screenshot di A S P . N E T Configuration tool per creare due utenti.

    Screenshot dello strumento di amministrazione del sito Web per gestire tutta la sicurezza.

  3. Creare un ruolo denominato Amministrazione e aggiungere 'oldAdminUser' come utente in tale ruolo.

    Screenshot del ruolo Amministrazione creato come utente in tale ruolo.

  4. Creare una sezione Amministrazione del sito con default.aspx. Impostare il tag di autorizzazione nel file di web.config per abilitare l'accesso solo agli utenti nei ruoli Amministrazione. Altre informazioni sono disponibili qui https://www.asp.net/web-forms/tutorials/security/roles/role-based-authorization-cs

    Screenshot della sezione Amministrazione del sito.

  5. Visualizzare il database in Esplora server per comprendere le tabelle create dal sistema di appartenenza SQL. I dati di accesso utente vengono archiviati nelle tabelle aspnet_Users e aspnet_Membership, mentre i dati del ruolo vengono archiviati nella tabella aspnet_Roles. Informazioni sugli utenti in cui sono archiviati i ruoli nella tabella aspnet_UsersInRoles. Per la gestione delle appartenenze di base, è sufficiente trasferire le informazioni nelle tabelle precedenti al sistema di gestione delle identità ASP.NET.

    Screenshot di Esplora server per visualizzare il database per comprendere le tabelle create dal sistema di appartenenza S Q L.

Migrazione a Visual Studio 2013

  1. Installare Visual Studio Express 2013 per Web o Visual Studio 2013 insieme agli aggiornamenti più recenti.

  2. Aprire il progetto precedente nella versione installata di Visual Studio. Se SQL Server Express non è installato nel computer, viene visualizzato un prompt quando si apre il progetto, poiché la stringa di connessione usa SQL Express. È possibile scegliere di installare SQL Express o come soluzione alternativa per modificare la stringa di connessione in LocalDb. Per questo articolo verrà modificato in LocalDb.

  3. Aprire web.config e modificare la stringa di connessione da . SQLExpress to (LocalDb)v11.0. Rimuovere 'User Instance=true' dalla stringa di connessione.

    Screenshot della modifica della stringa di connessione di cui eseguire la migrazione a Visual Studio 2013.

  4. Aprire Esplora server e verificare che sia possibile osservare lo schema e i dati della tabella.

  5. Il sistema di identità ASP.NET funziona con la versione 4.5 o successiva del framework. Ridestinare l'applicazione alla versione 4.5 o successiva.

    Screenshot di A S P . N E T Identity System.

    Compilare il progetto per verificare che non siano presenti errori.

Installazione dei pacchetti NuGet

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto >Gestisci pacchetti NuGet. Nella casella di ricerca immettere "Asp.net Identity". Selezionare il pacchetto nell'elenco dei risultati e fare clic su Installa. Accettare il contratto di licenza facendo clic sul pulsante "Accetto". Si noti che questo pacchetto installerà i pacchetti di dipendenza: EntityFramework e Microsoft ASP.NET Identity Core. Analogamente, installare i pacchetti seguenti (ignorare gli ultimi 4 pacchetti OWIN se non si vuole abilitare l'accesso 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

      Screenshot dell'installazione dei pacchetti NuGet in Esplora soluzioni.

Eseguire la migrazione del database al nuovo sistema di identità

Il passaggio successivo consiste nel eseguire la migrazione del database esistente a uno schema richiesto dal sistema di identità ASP.NET. A tale scopo, viene eseguito uno script SQL con un set di comandi per creare nuove tabelle ed eseguire la migrazione delle informazioni utente esistenti alle nuove tabelle. Il file di script è disponibile qui.

Questo file di script è specifico per questo esempio. Se lo schema per le tabelle create usando l'appartenenza SQL è personalizzato o modificato, è necessario modificare di conseguenza gli script.

Come generare lo script SQL per la migrazione dello schema

Per consentire ASP.NET classi Identity di usare i dati degli utenti esistenti, è necessario eseguire la migrazione dello schema del database a quello necessario per ASP.NET Identity. A tale scopo, è possibile aggiungere nuove tabelle e copiare le informazioni esistenti in tali tabelle. Per impostazione predefinita, ASP.NET Identity usa EntityFramework per eseguire il mapping delle classi del modello Identity al database per archiviare/recuperare informazioni. Queste classi di modello implementano le interfacce di base identity che definiscono gli oggetti utente e ruolo. Le tabelle e le colonne del database sono basate su queste classi di modello. Le classi del modello EntityFramework in Identity v2.1.0 e le relative proprietà sono definite di seguito

IdentityUser Tipo IdentityRole IdentityUserRole IdentityUserLogin IdentityUserClaim
ID string ID RoleId ProviderKey ID
Username string Nome UserId UserId ClaimType
PasswordHash string LoginProvider ClaimValue
SecurityStamp string User_id
Email string
Messaggio di posta elettronica Confermato bool
PhoneNumber string
PhoneNumber Confirm bool
BloccoEnabled bool
LockoutEndDate Datetime
AccessFailedCount INT

È necessario disporre di tabelle per ognuno di questi modelli con colonne corrispondenti alle proprietà. Il mapping tra classi e tabelle è definito nel OnModelCreating metodo di IdentityDBContext. Questo è noto come metodo API fluente di configurazione e altre informazioni sono disponibili qui. La configurazione per le classi è come indicato di seguito

Classe Tabella Chiave primaria Chiave esterna
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>

Con queste informazioni è possibile creare istruzioni SQL per creare nuove tabelle. È possibile scrivere ogni istruzione singolarmente o generare l'intero script usando i comandi di PowerShell EntityFramework che è quindi possibile modificare in base alle esigenze. A tale scopo, in VS aprire la console di Gestione pacchetti dal menu Visualizza o Strumenti

  • Eseguire il comando "Enable-Migrations" per abilitare le migrazioni entityFramework.
  • Eseguire il comando "Add-migration initial" che crea il codice di installazione iniziale per creare il database in C#/VB.
  • Il passaggio finale consiste nell'eseguire il comando "Update-Database -Script" che genera lo script SQL in base alle classi del modello.

Alcuni comandi non sono supportati se l'app usa SQLite come archivio dati Identity. A causa delle limitazioni nel motore di database, Alter i comandi generano l'eccezione seguente:

"System.NotSupportedException: SQLite non supporta questa operazione di migrazione".

In seguito, eseguire code First migrations nel database per modificare le tabelle.

Questo script di generazione di database può essere usato come inizio in cui verranno apportate modifiche aggiuntive per aggiungere nuove colonne e copiare i dati. Questo vantaggio è che viene generata la _MigrationHistory tabella usata da EntityFramework per modificare lo schema del database quando le classi di modello cambiano per le versioni future di Identity.

Le informazioni utente dell'appartenenza SQL hanno altre proprietà oltre a quelle della classe modello utente Identity, ovvero posta elettronica, tentativi di password, ultima data di accesso, data dell'ultimo blocco e così via. Si tratta di informazioni utili e si vuole che venga portata avanti nel sistema di identità. A tale scopo, è possibile aggiungere proprietà aggiuntive al modello utente e eseguirne il mapping alle colonne di tabella nel database. È possibile eseguire questa operazione aggiungendo una classe che sottoclasse il IdentityUser modello. È possibile aggiungere le proprietà a questa classe personalizzata e modificare lo script SQL per aggiungere le colonne corrispondenti durante la creazione della tabella. Il codice per questa classe è descritto ulteriormente nell'articolo. Lo script SQL per la creazione della tabella dopo l'aggiunta AspnetUsers delle nuove proprietà sarebbe

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

È quindi necessario copiare le informazioni esistenti dal database di appartenenza SQL alle tabelle appena aggiunte per Identity. Questa operazione può essere eseguita tramite SQL copiando i dati direttamente da una tabella a un'altra. Per aggiungere dati alle righe della tabella, viene usato il INSERT INTO [Table] costrutto. Per copiare da un'altra tabella è possibile usare l'istruzione insieme all'istruzione INSERT INTOSELECT . Per ottenere tutte le informazioni utente necessarie per eseguire query sulle tabelle aspnet_Users e aspnet_Membership e copiare i dati nella tabella AspNetUsers . Viene usato e INSERT INTOSELECT insieme alle JOIN istruzioni e LEFT OUTER JOIN . Per altre informazioni sull'esecuzione di query e sulla copia dei dati tra tabelle, vedere questo collegamento. Inoltre, le tabelle AspnetUserLogins e AspnetUserClaims sono vuote per iniziare perché non sono presenti informazioni nell'appartenenza SQL che esegue il mapping a questo valore per impostazione predefinita. L'unica informazione copiata è per gli utenti e i ruoli. Per il progetto creato nei passaggi precedenti, la query SQL per copiare le informazioni nella tabella utenti sarebbe

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;

Nell'istruzione SQL precedente vengono copiate informazioni su ogni utente dalla tabella aspnet_Users e aspnet_Membership nelle colonne della tabella AspnetUsers . L'unica modifica eseguita qui è quando si copia la password. Poiché l'algoritmo di crittografia per le password nell'appartenenza SQL ha usato "PasswordSalt" e "PasswordFormat", viene copiato troppo insieme alla password hash in modo che possa essere usata per decrittografare la password in base all'identità. Questo argomento viene spiegato ulteriormente nell'articolo quando si collega una password personalizzata hasher.

Questo file di script è specifico per questo esempio. Per le applicazioni che dispongono di tabelle aggiuntive, gli sviluppatori possono seguire un approccio simile per aggiungere altre proprietà nella classe modello utente e eseguirne il mapping alle colonne nella tabella AspnetUsers. Per eseguire lo script,

  1. Aprire Esplora server. Espandere la connessione 'ApplicationServices' per visualizzare le tabelle. Fare clic con il pulsante destro del mouse sul nodo Tabelle e selezionare l'opzione "Nuova query"

    Screenshot di Open Server Explorer per espandere la connessione servizi applicazioni per visualizzare le tabelle.

  2. Nella finestra di query copiare e incollare l'intero script SQL dal file Migrations.sql. Eseguire il file di script premendo il pulsante freccia "Esegui".

    Screenshot della finestra della query per copiare e incollare l'intero script S Q L.

    Aggiornare la finestra Esplora server. Cinque nuove tabelle vengono create nel database.

    Screenshot dell'aggiornamento della finestra Esplora server per creare cinque nuove tabelle nel database.

    Screenshot delle tabelle in Connessioni dati.

    Di seguito è riportato come vengono mappate le informazioni nelle tabelle di appartenenza SQL al nuovo sistema Identity.

    aspnet_Roles --> AspNetRoles

    asp_netUsers e asp_netMembership --> AspNetUsers

    aspnet_UserInRoles --> AspNetUserRoles

    Come illustrato nella sezione precedente, le tabelle AspNetUserClaims e AspNetUserLogins sono vuote. Il campo 'Discriminat' nella tabella AspNetUser deve corrispondere al nome della classe di modello definito come passaggio successivo. Anche la colonna PasswordHash è nel formato 'password crittografata |sale password|formato password'. In questo modo è possibile usare la logica di crittografia di appartenenza SQL speciale in modo che sia possibile riutilizzare le password precedenti. Questo è spiegato più avanti nell'articolo.

Creazione di modelli e pagine di appartenenza

Come accennato in precedenza, la funzionalità Identity usa Entity Framework per comunicare al database per archiviare le informazioni sull'account per impostazione predefinita. Per usare i dati esistenti nella tabella, è necessario creare classi di modello che vengono mappate di nuovo alle tabelle e collegarle nel sistema Identity. Come parte del contratto Identity, le classi di modello devono implementare le interfacce definite nella dll Identity.Core o estendere l'implementazione esistente di queste interfacce disponibili in Microsoft.AspNet.Identity.EntityFramework.

Nell'esempio, le tabelle AspNetRoles, AspNetUserClaims, AspNetLogins e AspNetUserRole presentano colonne simili all'implementazione esistente del sistema Identity. Di conseguenza, è possibile riutilizzare le classi esistenti per eseguire il mapping a queste tabelle. La tabella AspNetUser include alcune colonne aggiuntive usate per archiviare informazioni aggiuntive dalle tabelle di appartenenza SQL. È possibile eseguire il mapping creando una classe modello che estende l'implementazione esistente di 'IdentityUser' e aggiungere le proprietà aggiuntive.

  1. Creare una cartella Models nel progetto e aggiungere un utente di classe. Il nome della classe deve corrispondere ai dati aggiunti nella colonna 'Discriminat' della tabella 'AspnetUsers'.

    Screenshot della creazione di una cartella Models nel progetto e dell'aggiunta di un utente di classe.

    La classe User deve estendere la classe IdentityUser trovata nella dll Microsoft.AspNet.Identity.EntityFramework . Dichiarare le proprietà nella classe che vengono mappate alle colonne AspNetUser. Le proprietà ID, Username, PasswordHash e SecurityStamp sono definite nell'identityUser e quindi vengono omesse. Di seguito è riportato il codice per la classe User con tutte le proprietà

    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. È necessaria una classe DbContext di Entity Framework per rendere persistenti i dati nei modelli nelle tabelle e recuperare i dati dalle tabelle per popolare i modelli. Microsoft.AspNet.Identity.EntityFramework definisce la classe IdentityDbContext che interagisce con le tabelle Identity per recuperare e archiviare le informazioni. Il tuser> IdentityDbContext<accetta una classe 'TUser' che può essere qualsiasi classe che estende la classe IdentityUser.

    Creare una nuova classe ApplicationDBContext che estende IdentityDbContext nella cartella 'Models' passando la classe 'User' creata nel passaggio 1

    public class ApplicationDbContext : IdentityDbContext<User>
    {
            
    }
    
  3. La gestione degli utenti nel nuovo sistema Identity viene eseguita usando la classe tuser> UserManager<definita nella dll Microsoft.AspNet.Identity.EntityFramework. È necessario creare una classe personalizzata che estende UserManager, passando la classe 'User' creata nel passaggio 1.

    Nella cartella Models creare una nuova classe UserManager che estende l'utente userManager<>

    public class UserManager : UserManager<User>
    {
            
    }
    
  4. Le password degli utenti dell'applicazione vengono crittografate e archiviate nel database. L'algoritmo di crittografia usato nell'appartenenza a SQL è diverso da quello del nuovo sistema Identity. Per riutilizzare le password precedenti, è necessario decrittografare in modo selettivo le password quando gli utenti precedenti accedono usando l'algoritmo di appartenenza SQL usando l'algoritmo di crittografia in Identity per i nuovi utenti.

    La classe UserManager ha una proprietà 'PasswordHasher' che archivia un'istanza di una classe che implementa l'interfaccia 'IPasswordHasher'. Viene usato per crittografare/decrittografare le password durante le transazioni di autenticazione utente. Nella classe UserManager definita nel passaggio 3 creare una nuova classe SQLPasswordHasher e copiare il codice seguente.

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

    Risolvere gli errori di compilazione importando gli spazi dei nomi System.Text e System.Security.Cryptography.

    Il metodo EncodePassword crittografa la password in base all'implementazione predefinita della crittografia di appartenenza SQL. Questa operazione viene presa dalla dll System.Web. Se l'app precedente ha usato un'implementazione personalizzata, dovrebbe essere riflessa qui. È necessario definire due altri metodi HashPassword e VerifyHashedPassword che usano il metodo EncodePassword per hashare una determinata password o verificare una password di testo normale con quella esistente nel database.

    Il sistema di appartenenza SQL ha usato PasswordHash, PasswordSalt e PasswordFormat per hashare la password immessa dagli utenti quando registrano o modificano la password. Durante la migrazione tutti i tre campi vengono archiviati nella colonna PasswordHash nella tabella AspNetUser separata dal carattere '|'. Quando un utente accede e la password dispone di questi campi, si usa la crittografia di appartenenza SQL per controllare la password; in caso contrario, si usa la crittografia predefinita del sistema identity per verificare la password. In questo modo gli utenti precedenti non dovranno modificare le password dopo la migrazione dell'app.

  5. Dichiarare il costruttore per la classe UserManager e passarlo come SQLPasswordHasher alla proprietà nel costruttore.

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

Creare nuove pagine di gestione account

Il passaggio successivo della migrazione consiste nell'aggiungere pagine di gestione degli account che consentono a un utente di registrare e accedere. Le pagine dell'account precedente dell'appartenenza SQL usano i controlli che non funzionano con il nuovo sistema Di identità. Per aggiungere le nuove pagine di gestione utenti, seguire l'esercitazione in questo collegamento https://www.asp.net/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project a partire dal passaggio "Aggiunta di Web Forms per la registrazione degli utenti all'applicazione" poiché è già stato creato il progetto e aggiunto i pacchetti NuGet.

È necessario apportare alcune modifiche per l'uso dell'esempio con il progetto in uso.

  • Le classi code behind Register.aspx.cs e Login.aspx.cs usano i UserManager pacchetti Identity per creare un utente. Per questo esempio, usare UserManager aggiunto nella cartella Modelli seguendo i passaggi indicati in precedenza.

  • Usare la classe User creata anziché le classi IdentityUser in Register.aspx.cs e Login.aspx.cs code behind. Questo si aggancia alla classe utente personalizzata nel sistema Identity.

  • La parte da creare il database può essere ignorata.

  • Lo sviluppatore deve impostare ApplicationId per il nuovo utente in modo che corrisponda all'ID applicazione corrente. Questa operazione può essere eseguita eseguendo una query su ApplicationId per questa applicazione prima che venga creato un oggetto utente nella classe Register.aspx.cs e impostandola prima di creare l'utente.

    Esempio:

    Definire un metodo nella pagina Register.aspx.cs per eseguire query sulla tabella aspnet_Applications e ottenere l'ID applicazione in base al nome dell'applicazione

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

    A questo scopo, impostare questo oggetto sull'oggetto utente

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

Usare il nome utente e la password precedenti per accedere a un utente esistente. Usare la pagina Registra per creare un nuovo utente. Verificare anche che gli utenti si trovino in ruoli come previsto.

La conversione nel sistema Identity consente all'utente di aggiungere l'autenticazione aperta (OAuth) all'applicazione. Fare riferimento all'esempio qui che dispone di OAuth abilitato.

Passaggi successivi

In questa esercitazione è stato illustrato come convertire gli utenti dall'appartenenza SQL all'identità ASP.NET, ma non sono stati visualizzati i dati del profilo di porta. Nell'esercitazione successiva verranno esaminati i dati del profilo di conversione dall'appartenenza SQL al nuovo sistema di identità.

È possibile lasciare commenti e suggerimenti nella parte inferiore di questo articolo.

Grazie a Tom Dykstra e Rick Anderson per rivedere l'articolo.