Share via


Migrar un sitio web existente desde la pertenencia de SQL a ASP.NET Identity

por Rick Anderson, Suhas Joshi

En este tutorial se muestran los pasos para migrar una aplicación web existente con datos de usuario y roles creados mediante la pertenencia a SQL al nuevo sistema ASP.NET Identity. Este enfoque implica cambiar el esquema de base de datos existente al que necesita ASP.NET Identity y enlazarlo en las clases antiguas o nuevas a ella. Después de adoptar este enfoque, una vez migrada la base de datos, las actualizaciones futuras de Identity se controlarán sin esfuerzo.

En este tutorial, tomaremos una plantilla de aplicación web (Web Forms) creada con Visual Studio 2010 para crear datos de usuario y rol. Después, usaremos scripts de SQL para migrar la base de datos existente a las tablas necesarias para el sistema de identidades. A continuación, instalaremos los paquetes NuGet necesarios y agregaremos nuevas páginas de administración de cuentas que usan el sistema Identity para la administración de pertenencia. Como prueba de la migración, los usuarios creados mediante la pertenencia a SQL deberían poder iniciar sesión y los nuevos usuarios deberían poder registrarse. Puede encontrar el ejemplo completo aquí. Consulte también Migración de la pertenencia a ASP.NET a ASP.NET Identity.

Introducción

Creación de una aplicación con pertenencia a SQL

  1. Es necesario empezar con una aplicación existente que use la pertenencia a SQL y que tenga datos de usuario y rol. Para este artículo, vamos a crear una aplicación web en Visual Studio 2010.

    Screenshot of creating a web application in Visual Studio 2010.

  2. Con la herramienta de configuración de ASP.NET, cree 2 usuarios: oldAdminUser y oldUser.

    Screenshot of the A S P . N E T Configuration tool to create two users.

    Screenshot of the website administration tool to manage all the security.

  3. Cree un rol denominado Admin y agregue "oldAdminUser" como usuario en ese rol.

    Screenshot of the Admin role created as a user in that role.

  4. Cree una sección Admin del sitio con un Default.aspx. Establezca la etiqueta de autorización en el archivo web.config para habilitar el acceso solo a los usuarios en roles de administrador. Puede encontrar más información aquí https://www.asp.net/web-forms/tutorials/security/roles/role-based-authorization-cs

    Screenshot of the site's Admin section.

  5. Vea la base de datos en el Explorador de servidores para comprender las tablas creadas por el sistema de pertenencia a SQL. Los datos de inicio de sesión de usuario se almacenan en las tablas aspnet_Users y aspnet_Membership, mientras que los datos de rol se almacenan en la tabla aspnet_Roles. La información sobre qué usuarios están en cuáles roles se almacena en la tabla aspnet_UsersInRoles. Para la administración de pertenencia básica, es suficiente migrar la información de las tablas anteriores al sistema ASP.NET Identity.

    Screenshot of the Server Explorer to view the database to understand the tables created by the S Q L membership system.

Migración a Visual Studio 2013

  1. Instale Visual Studio Express 2013 para Web o Visual Studio 2013 junto con las actualizaciones más recientes.

  2. Abra el proyecto anterior en la versión instalada de Visual Studio. Si SQL Server Express no está instalado en el equipo, se muestra un mensaje al abrir el proyecto, ya que la cadena de conexión usa SQL Express. Puede optar por instalar SQL Express o, como solución alternativa, cambiar la cadena de conexión a LocalDb. En este artículo se cambiará a LocalDb.

  3. Abra web.config y cambie la cadena de conexión de .SQLExpress a (LocalDb)v11.0. Quite "User Instance=true" de la cadena de conexión.

    Screenshot of changing the connection string to migrate to Visual Studio 2013.

  4. Abra el Explorador de servidores y compruebe que se pueden observar los datos y el esquema de la tabla.

  5. El sistema ASP.NET Identity funciona con la versión 4.5 o posterior del marco. Vuelva a establecer la aplicación en 4.5 o superior.

    Screenshot of the A S P . N E T Identity system.

    Compile el proyecto para comprobar que no hay ningún error.

Instalación de los paquetes Nuget

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto >Administrar paquetes NuGet. En el cuadro de búsqueda, escriba "Asp.net Identity". Seleccione el paquete en la lista de resultados y haga clic en Instalar. Acepte el contrato de licencia haciendo clic en el botón "Acepto". Tenga en cuenta que este paquete instalará los paquetes de dependencia: EntityFramework y Microsoft ASP.NET Identity Core. De forma similar, instale los siguientes paquetes (omita los últimos 4 paquetes OWIN si no desea habilitar el inicio de sesión de 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 of installing the Nuget packages in Solution Explorer.

Migración de la base de datos al nuevo sistema Identity

El siguiente paso consiste en migrar la base de datos existente a un esquema requerido por el sistema ASP.NET Identity. Para lograr esto, ejecutamos un script SQL que tiene un conjunto de comandos para crear nuevas tablas y migrar la información de usuario existente a las nuevas tablas. El archivo de script se puede encontrar aquí.

Este archivo de script es específico de este ejemplo. Si el esquema de las tablas creadas mediante la pertenencia a SQL se personaliza o modifica, los scripts deben cambiarse en consecuencia.

Cómo generar el script SQL para la migración de esquemas

Para que las clases de ASP.NET Identity funcionen de forma predeterminada con los datos de los usuarios existentes, es necesario migrar el esquema de la base de datos a la que necesita ASP.NET Identity. Podemos hacerlo agregando nuevas tablas y copiando la información existente en esas tablas. De forma predeterminada, ASP.NET Identity usa EntityFramework para volver a asignar las clases del modelo Identity a la base de datos para almacenar o recuperar información. Estas clases de modelo implementan las interfaces de Identity principales que definen objetos de usuario y roles. Las tablas y las columnas de la base de datos se basan en estas clases de modelo. Las clases de modelo EntityFramework de Identity v2.1.0 y sus propiedades son como se definen a continuación

IdentityUser Tipo IdentityRole IdentityUserRole IdentityUserLogin IdentityUserClaim
Identificador string Identificador RoleId ProviderKey Identificador
Nombre de usuario string Nombre UserId UserId ClaimType
PasswordHash string LoginProvider ClaimValue
SecurityStamp string User_Id
Email string
EmailConfirmed bool
PhoneNumber string
PhoneNumberConfirmed bool
LockoutEnabled bool
LockoutEndDate DateTime
AccessFailedCount int

Es necesario tener tablas para cada uno de estos modelos con columnas correspondientes a las propiedades. La asignación entre clases y tablas se define en el método OnModelCreating de IdentityDBContext. Esto se conoce como el método de API fluida de configuración y puede encontrar más información aquí. La configuración de las clases es como se menciona a continuación

Clase Tabla Clave principal Clave externa
IdentityUser AspnetUsers Identificador
IdentityRole AspnetRoles Identificador
IdentityUserRole AspnetUserRole UserId + RoleId User_Id->AspnetUsers RoleId->AspnetRoles
IdentityUserLogin AspnetUserLogins ProviderKey+UserId + LoginProvider UserId->AspnetUsers
IdentityUserClaim AspnetUserClaims Identificador User_Id->AspnetUsers

Con esta información, podemos crear instrucciones SQL para crear nuevas tablas. Podemos escribir cada instrucción individualmente o generar todo el script mediante comandos de PowerShell EntityFramework que, a continuación, podemos editar según sea necesario. Para ello, en VS, abra la consola del Administrador de paquetes desde el menú Ver o Herramientas.

  • Ejecute el comando "Enable-Migrations" para habilitar las migraciones de EntityFramework.
  • Ejecute el comando "Add-migration initial", que crea el código de instalación inicial para crear la base de datos en C#/VB.
  • El último paso es ejecutar el comando "Update-Database –Script" que genera el script SQL basado en las clases de modelo.

Algunos comandos no se admiten si la aplicación usa SQLite como almacén de datos de Identity. Debido a las limitaciones del motor de base de datos, los comandos Alter producen la siguiente excepción:

"System.NotSupportedException: SQLite no admite esta operación de migración"

Como solución alternativa, ejecute migraciones de Code First en la base de datos para cambiar las tablas.

Este script de generación de base de datos se puede usar como un inicio en el que realizaremos cambios adicionales para agregar nuevas columnas y copiar datos. La ventaja de esto es que generamos la tabla _MigrationHistory que usa EntityFramework para modificar el esquema de la base de datos cuando cambien las clases de modelo para futuras versiones de Identity.

La información del usuario de pertenencia a SQL tenía otras propiedades además de las de la clase modelo de usuario de Identity, es decir, correo electrónico, intentos de contraseña, fecha de último inicio de sesión, última fecha de bloqueo, etc. Esta información es útil y nos gustaría que se llevara al sistema Identity. Esto se puede hacer agregando propiedades adicionales al modelo de usuario y asignándolas de nuevo a las columnas de tabla de la base de datos. Podemos hacerlo agregando una clase que subclasifique el modelo IdentityUser. Podemos agregar las propiedades a esta clase personalizada y editar el script SQL para agregar las columnas correspondientes al crear la tabla. El código de esta clase se describe más adelante en el artículo. El script SQL para crear la tabla AspnetUsers después de agregar las nuevas propiedades sería

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

A continuación, es necesario copiar la información existente de la base de datos de pertenencia a SQL a las tablas recién agregadas para Identity. Esto se puede hacer a través de SQL copiando los datos directamente de una tabla a otra. Para agregar datos a las filas de la tabla, usamos la construcción INSERT INTO [Table]. Para copiar desde otra tabla, podemos usar la instrucción INSERT INTO junto con la instrucción SELECT. Para obtener toda la información del usuario, necesitamos consultar las tablas aspnet_Users y aspnet_Membership y copiar los datos en la tabla AspNetUsers. Usamos INSERT INTO y SELECT junto con las instrucciones JOIN y LEFT OUTER JOIN. Para obtener más información sobre cómo consultar y copiar datos entre tablas, consulte este vínculo. Además, las tablas AspnetUserLogins y AspnetUserClaims están vacías para comenzar, ya que no hay información en la pertenencia a SQL que se asigne a estas de forma predeterminada. La única información copiada es para usuarios y roles. Para el proyecto creado en los pasos anteriores, la consulta de SQL para copiar información en la tabla de usuario sería

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;

En la instrucción SQL anterior, se copia información sobre cada usuario de las tablas aspnet_Users y aspnet_Membership en las columnas de la tabla AspnetUsers. La única modificación realizada aquí es cuando copiamos la contraseña. Dado que el algoritmo de cifrado de contraseñas de la pertenencia a SQL usó "PasswordSalt" y "PasswordFormat", lo copiamos también junto con la contraseña con hash para que se pueda usar para descifrar la contraseña mediante Identity. Esto se explica más adelante en el artículo al enlazar el hash de una contraseña personalizada.

Este archivo de script es específico de este ejemplo. En el caso de las aplicaciones que tienen tablas adicionales, los desarrolladores pueden seguir un enfoque similar para agregar propiedades adicionales en la clase de modelo de usuario y asignarlas a columnas de la tabla AspnetUsers. Para ejecutar el script,

  1. Abra el Explorador de servidores. Expanda la conexión "ApplicationServices" para mostrar las tablas. Haga clic con el botón derecho en el nodo Tablas y seleccione la opción "Nueva consulta"

    Screenshot of the Open Server Explorer to expand the Application Services connection to display the tables.

  2. En la ventana de consulta, copie y pegue todo el script SQL del archivo Migrations.sql. Ejecute el archivo de script presionando el botón de flecha "Ejecutar".

    Screenshot of the query window to copy and paste the entire S Q L script.

    Actualice la ventana Explorador de servidores. Se crean cinco nuevas tablas en la base de datos.

    Screenshot of refreshing the Server Explorer window to create five new tables in the database.

    Screenshot of the Tables in Data Connections.

    A continuación se muestra cómo se asigna la información de las tablas de pertenencia a SQL al nuevo sistema Identity.

    aspnet_Roles --> AspNetRoles

    asp_netUsers and asp_netMembership --> AspNetUsers

    aspnet_UserInRoles --> AspNetUserRoles

    Como se explicó en la sección anterior, las tablas AspNetUserClaims y AspNetUserLogins están vacías. El campo "Discriminador" de la tabla AspNetUser debe coincidir con el nombre de clase de modelo que se define como paso siguiente. Además, la columna PasswordHash tiene el formato "contraseña cifrada |contraseña salt|formato de contraseña". Esto le permite usar una lógica criptográfica de pertenencia a SQL especial para poder reutilizar contraseñas antiguas. Esto se explica más adelante en el artículo.

Creación de modelos y páginas de pertenencia

Como se mencionó anteriormente, la característica Identity usa Entity Framework para comunicarse con la base de datos para almacenar información de la cuenta de forma predeterminada. Para trabajar con datos existentes en la tabla, es necesario crear clases de modelo que se asignen de nuevo a las tablas y enlazarlos en el sistema Identity. Como parte del contrato Identity, las clases de modelo deben implementar las interfaces definidas en el archivo dll Identity.Core o pueden ampliar la implementación existente de estas interfaces disponibles en Microsoft.AspNet.Identity.EntityFramework.

En nuestro ejemplo, las tablas AspNetRoles, AspNetUserClaims, AspNetLogins y AspNetUserRole tienen columnas similares a la implementación existente del sistema Identity. Por lo tanto, podemos reutilizar las clases existentes para asignarlas a estas tablas. La tabla AspNetUser tiene algunas columnas adicionales que se usan para almacenar información adicional de las tablas de pertenencia a SQL. Esto se puede asignar mediante la creación de una clase de modelo que extienda la implementación existente de "IdentityUser" y agregue las propiedades adicionales.

  1. Cree una carpeta Models en el proyecto y agregue una clase User. El nombre de la clase debe coincidir con los datos agregados en la columna "Discriminador" de la tabla "AspnetUsers".

    Screenshot of creating a Models folder in the project and adding a class User.

    La clase User debe extender la clase IdentityUser que se encuentra en el archivo dll Microsoft.AspNet.Identity.EntityFramework. Declare las propiedades de la clase que se asignan a las columnas AspNetUser. El ID, Username, PasswordHash y SecurityStamp de las propiedades se definen en IdentityUser, por lo que se omiten. A continuación se muestra el código de la clase User que tiene todas las propiedades

    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. Se requiere una clase DbContext de Entity Framework para conservar los datos de los modelos de vuelta a las tablas y recuperar los datos de las tablas para rellenar los modelos. El archivo dll Microsoft.AspNet.Identity.EntityFramework define la clase IdentityDbContext que interactúa con las tablas Identity para recuperar y almacenar información. El tuser>IdentityDbContext< toma una clase "TUser", que puede ser cualquier clase que extienda la clase IdentityUser.

    Cree una nueva clase ApplicationDBContext que extienda IdentityDbContext en la carpeta "Models", pasando la clase "User" creada en el paso 1.

    public class ApplicationDbContext : IdentityDbContext<User>
    {
            
    }
    
  3. La administración de usuarios en el nuevo sistema Identity se realiza mediante la clase UserManager<tuser> definida en el archivo dll Microsoft.AspNet.Identity.EntityFramework. Es necesario crear una clase personalizada que extienda UserManager y pase la clase "User" creada en el paso 1.

    En la carpeta Models, cree una nueva clase UserManager que extienda UserManager<user>

    public class UserManager : UserManager<User>
    {
            
    }
    
  4. Las contraseñas de los usuarios de la aplicación se cifran y almacenan en la base de datos. El algoritmo criptográfico usado en la pertenencia a SQL es diferente del nuevo en el sistema Identity. Para reutilizar contraseñas antiguas, es necesario descifrar de forma selectiva las contraseñas cuando los usuarios antiguos inician sesión con el algoritmo de pertenencias a SQL mientras se usa el algoritmo criptográfico en Identity para los nuevos usuarios.

    La clase UserManager tiene una propiedad "PasswordHasher" que almacena una instancia de una clase que implementa la interfaz "IPasswordHasher". Se usa para cifrar o descifrar contraseñas durante las transacciones de autenticación de usuario. En la clase UserManager definida en el paso 3, cree una nueva clase SQLPasswordHasher y copie el código siguiente.

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

    Resuelva los errores de compilación importando los espacios de nombres System.Text y System.Security.Cryptography.

    El método EncodePassword cifra la contraseña según la implementación de criptografía de pertenencia a SQL predeterminada. Esto se toma del archivo dll System.Web. Si la aplicación antigua usó una implementación personalizada, debe reflejarse aquí. Necesitamos definir otros dos métodos HashPassword y VerifyHashedPassword que usan el método EncodePassword para aplicar un hash a una contraseña determinada o comprobar una contraseña de texto sin formato con la existente en la base de datos.

    El sistema de pertenencia a SQL usó PasswordHash, PasswordSalt y PasswordFormat para aplicar un hash a la contraseña especificada por los usuarios al registrar o cambiar su contraseña. Durante la migración, los tres campos se almacenan en la columna PasswordHash de la tabla AspNetUser separada por el carácter '|'. Cuando un usuario inicia sesión y la contraseña tiene estos campos, usamos la criptografía de pertenencia a SQL para comprobar la contraseña. De lo contrario, usamos la criptografía predeterminada del sistema de Identity para comprobar la contraseña. De este modo, los usuarios antiguos no tendrán que cambiar sus contraseñas una vez que se migre la aplicación.

  5. Declare el constructor para la clase UserManager y pásela como SQLPasswordHasher a la propiedad del constructor.

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

Creación de páginas de administración de cuentas

El siguiente paso de la migración es agregar páginas de administración de cuentas que permitirán que un usuario se registre e inicie sesión. Las páginas de cuenta antiguas de la pertenencia a SQL usan controles que no funcionan con el nuevo sistema de identidades. Para agregar las nuevas páginas de administración de usuarios, siga el tutorial en este vínculo https://www.asp.net/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project a partir del paso "Agregar Web Forms para registrar usuarios en la aplicación", ya que ya hemos creado el proyecto y agregado los paquetes NuGet.

Es necesario realizar algunos cambios para que el ejemplo funcione con el proyecto que tenemos aquí.

  • El código Register.aspx.cs y Login.aspx.cs subyacente usan UserManager desde los paquetes de Identity para crear un User. Para este ejemplo, use el UserManager agregado en la carpeta Models siguiendo los pasos mencionados anteriormente.

  • Use la clase User creada en lugar de IdentityUser en el código Register.aspx.cs y Login.aspx.cs subyacente. Esto enlaza en nuestra clase de usuario personalizada al sistema Identity.

  • Se puede omitir la parte en que se va a crear la base de datos.

  • El desarrollador debe establecer ApplicationId para que el nuevo usuario coincida con el id. de aplicación actual. Esto se puede hacer consultando ApplicationId para esta aplicación antes de crear un objeto de usuario en la clase Register.aspx.cs y establecerlo antes de crear el usuario.

    Ejemplo:

    Defina un método en la página Register.aspx.cs para consultar la tabla aspnet_Applications y obtener el id. de aplicación según el nombre de la aplicación

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

    Ahora, establézcalo en el objeto de usuario

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

Use el nombre de usuario y la contraseña antiguos para iniciar sesión en un usuario existente. Use la página Registrar para crear un nuevo usuario. Compruebe también que los usuarios están en roles según lo previsto.

La portabilidad al sistema Identity ayuda al usuario a agregar Open Authentication (OAuth) a la aplicación. Consulte aquí el ejemplo que tiene OAuth habilitado.

Pasos siguientes

En este tutorial se ha mostrado cómo portar usuarios desde la pertenencia de SQL a ASP.NET Identity, pero sin portar los datos de perfil. En el siguiente tutorial veremos cómo portar datos de perfil desde la pertenencia de SQL al nuevo sistema Identity.

Puede dejar sus comentarios en la parte inferior de este artículo.

Gracias a Tom Dykstra y Rick Anderson por revisar el artículo.