Compartilhar via


Migrar da autenticação de Associação do ASP.NET para a Identity do ASP.NET Core 2.0

Por Isaac Levin

Este artigo demonstra como migrar o esquema de banco de dados para aplicativos ASP.NET usando a autenticação de Associação para a Identity do ASP.NET Core 2.0.

Observação

Este documento fornece as etapas necessárias para migrar o esquema de banco de dados para aplicativos baseados em Associação do ASP.NET para o esquema de banco de dados usado para a Identity do ASP.NET Core. Para obter mais informações sobre como migrar da autenticação baseada em Associação do ASP.NET para a Identity do ASP.NET, consulte Migrar um aplicativo existente da Associação do SQL para a Identity do ASP.NET. Para obter mais informações sobre a Identity do ASP.NET Core, confira Introdução à Identity no ASP.NET Core.

Revisão do esquema de Associação

Antes do ASP.NET 2.0, os desenvolvedores eram encarregados de criar todo o processo de autenticação e autorização para seus aplicativos. Com o ASP.NET 2.0, a Associação foi introduzida, fornecendo uma solução clichê para lidar com a segurança dentro de aplicativos ASP.NET. Os desenvolvedores puderam então inicializar um esquema em um banco de dados do SQL Server com a Ferramenta de Registro do SQL Server no ASP.NET (Aspnet_regsql.exe) (não há mais suporte). Depois de executar esse comando, as tabelas a seguir foram criadas no banco de dados.

Tabelas de Associação

Para migrar aplicativos existentes para a Identity do ASP.NET Core 2.0, os dados nessas tabelas precisam ser migrados para as tabelas usadas pelo novo esquema de Identity.

Esquema de Identity do ASP.NET Core 2.0

O ASP.NET Core 2.0 segue o princípio Identity introduzido no ASP.NET 4.5. Embora o princípio seja compartilhado, a implementação entre as estruturas é diferente, mesmo entre versões do ASP.NET Core (consulte Migrar autenticação e Identity para o ASP.NET Core 2.0).

A maneira mais rápida de exibir o esquema para a Identity do ASP.NET Core 2.0 é criar um novo aplicativo ASP.NET Core 2.0. Siga estas etapas no Visual Studio 2017:

  1. Selecione Arquivo>Novo>Projeto.

  2. Crie um novo projeto do Aplicativo Web ASP.NET Core nomeado CoreIdentitySample.

  3. Selecione ASP.NET Core 2.0 na lista suspensa e, em seguida, selecione Aplicativo Web. Esse modelo produz um aplicativo PáginasRazor . Antes de clicar em OK, clique em Alterar Autenticação.

  4. Escolha Contas de Usuário Individuais para os modelos de Identity. Por fim, clique em OK e, em seguida, em OK. O Visual Studio cria um projeto usando o modelo de Identity do ASP.NET Core.

  5. Selecione Ferramentas>Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes para abrir o PMC (Console do Gerenciador de Pacotes) na janela.

  6. Navegue até a raiz do projeto no PMC e execute o comando Entity Framework CoreUpdate-Database.

    A Identity do ASP.NET Core 2.0 usa o EF Core para interagir com o banco de dados que armazena os dados de autenticação. Para que o aplicativo recém-criado funcione, é necessário que haja um banco de dados para armazenar esses dados. Depois de criar um novo aplicativo, a maneira mais rápida de inspecionar o esquema em um ambiente de banco de dados é criar o banco de dados usando EF Core Migrações. Esse processo cria um banco de dados, localmente ou em outro lugar, que imita esse esquema. Revise a documentação anterior para obter mais informações.

    Os comandos do EF Core usam a cadeia de conexão para o banco de dados especificado em appsettings.json. A cadeia de conexão a seguir tem como destino um banco de dados no localhost chamado asp-net-core-identity. Nessa configuração, o EF Core é configurado para usar a cadeia de conexão DefaultConnection.

    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=localhost;Database=aspnet-core-identity;Trusted_Connection=True;MultipleActiveResultSets=true"
      }
    }
    

Aviso

Este artigo mostra o uso de cadeias de conexão. Com um banco de dados local, o usuário não precisa ser autenticado, mas na produção, as cadeias de conexão às vezes incluem uma senha para autenticação. Uma credencial de senha do proprietário do recurso (ROPC) é um risco de segurança que deve ser evitado em bancos de dados de produção. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos implantados em ambientes de teste ou produção, consulte Fluxos de autenticação seguros.

  1. Selecione Exibir>Pesquisador de Objetos do SQL Server. Expanda o nó correspondente ao nome do banco de dados especificado na propriedade ConnectionStrings:DefaultConnection de appsettings.json.

    O comando Update-Database criou o banco de dados especificado com o esquema e todos os dados necessários para a inicialização do aplicativo. A imagem a seguir ilustra a estrutura da tabela criada com as etapas anteriores.

    Identity Tabelas

Migrar o esquema

Há diferenças sutis nas estruturas de tabela e nos campos para Associação e para a Identity do ASP.NET Core. O padrão mudou substancialmente para autenticação/autorização com aplicativos ASP.NET e ASP.NET Core. Os principais objetos que ainda são usados com a Identity são Usuários e Funções. Aqui estão as tabelas de mapeamento para Usuários, Funções e UserRoles.

Usuários

Identity
Coluna (dbo.AspNetUsers)
Type Associação
Coluna (dbo.aspnet_Users / dbo.aspnet_Membership)
Type
Id string aspnet_Users.UserId string
UserName string aspnet_Users.UserName string
Email string aspnet_Membership.Email string
NormalizedUserName string aspnet_Users.LoweredUserName string
NormalizedEmail string aspnet_Membership.LoweredEmail string
PhoneNumber string aspnet_Users.MobileAlias string
LockoutEnabled bit aspnet_Membership.IsLockedOut bit

IsLockedOut não é mapeado para LockoutEnabled. IsLockedOut será definido se um usuário tiver muitos logons com falha e estiver bloqueado por um tempo definido. LockoutEnabled permite bloquear um usuário com muitas tentativas de entrada com falha. Quando o usuário tiver muitas tentativas de entrada com falha, LockoutEnd será definido como uma data no futuro e o usuário não poderá entrar até essa data. Se LockoutEnabled for false, um usuário nunca será bloqueado por muitas tentativas de entrada com falha. De acordo com o OWASP, o bloqueio de conta temporário após várias tentativas com falha é um alvo muito simples para ataques DoS contra usuários legítimos.

Para obter mais informações sobre bloqueio, consulte Teste do OWASP para Mecanismo de Bloqueio Fraco.

Os aplicativos que migram para a Identity que desejam habilitar o bloqueio de logon com falha devem definir LockoutEnabled como true como parte da migração.

Observação

Nem todos os mapeamentos de campo se assemelham a relações um-para-um de Associação para a Identity do ASP.NET Core. A tabela anterior usa o esquema de Usuário Associado padrão e mapeia-o para o esquema de Identity do ASP.NET Core. Todos os outros campos personalizados que foram usados para Associação precisam ser mapeados manualmente. Neste mapeamento, não há nenhum mapa para senhas, pois os critérios e sais de senha não migram entre os dois. É recomendável deixar a senha como nula e pedir aos usuários que redefinam suas senhas. Na Identity do ASP.NET Core, LockoutEnd deverá ser definido como alguma data no futuro se o usuário estiver bloqueado. Isso é mostrado no script de migração.

Funções

Identity
Coluna (dbo.AspNetRoles)
Type Associação
Coluna (dbo.aspnet_Roles)
Type
Id string RoleId string
Name string RoleName string
NormalizedName string LoweredRoleName string

Funções de usuário

Identity
Coluna (dbo.AspNetUserRoles)
Type Associação
Coluna (dbo.aspnet_UsersInRoles)
Type
RoleId string RoleId string
UserId string UserId string

Referencie as tabelas de mapeamento anteriores ao criar um script de migração para Usuários e Funções. O exemplo a seguir pressupõe que você tenha dois bancos de dados em um servidor de banco de dados. Um banco de dados contém os dados e o esquema de Associação do ASP.NET existentes. O outro banco de dados CoreIdentitySample foi criado usando as etapas descritas anteriormente. Os comentários são incluídos em linha para obter mais detalhes.

-- THIS SCRIPT NEEDS TO RUN FROM THE CONTEXT OF THE MEMBERSHIP DB
BEGIN TRANSACTION MigrateUsersAndRoles
USE aspnetdb

-- INSERT USERS
INSERT INTO CoreIdentitySample.dbo.AspNetUsers
            (Id,
             UserName,
             NormalizedUserName,
             PasswordHash,
             SecurityStamp,
             EmailConfirmed,
             PhoneNumber,
             PhoneNumberConfirmed,
             TwoFactorEnabled,
             LockoutEnd,
             LockoutEnabled,
             AccessFailedCount,
             Email,
             NormalizedEmail)
SELECT aspnet_Users.UserId,
       aspnet_Users.UserName,
       -- The NormalizedUserName value is upper case in ASP.NET Core Identity
       UPPER(aspnet_Users.UserName),
       -- Creates an empty password since passwords don't map between the 2 schemas
       '',
       /*
        The SecurityStamp token is used to verify the state of an account and
        is subject to change at any time. It should be initialized as a new ID.
       */
       NewID(),
       /*
        EmailConfirmed is set when a new user is created and confirmed via email.
        Users must have this set during migration to reset passwords.
       */
       1,
       aspnet_Users.MobileAlias,
       CASE
         WHEN aspnet_Users.MobileAlias IS NULL THEN 0
         ELSE 1
       END,
       -- 2FA likely wasn't setup in Membership for users, so setting as false.
       0,
       CASE
         -- Setting lockout date to time in the future (1,000 years)
         WHEN aspnet_Membership.IsLockedOut = 1 THEN Dateadd(year, 1000,
                                                     Sysutcdatetime())
         ELSE NULL
       END,
       aspnet_Membership.IsLockedOut,
       /*
        AccessFailedAccount is used to track failed logins. This is stored in
        Membership in multiple columns. Setting to 0 arbitrarily.
       */
       0,
       aspnet_Membership.Email,
       -- The NormalizedEmail value is upper case in ASP.NET Core Identity
       UPPER(aspnet_Membership.Email)
FROM   aspnet_Users
       LEFT OUTER JOIN aspnet_Membership
                    ON aspnet_Membership.ApplicationId =
                       aspnet_Users.ApplicationId
                       AND aspnet_Users.UserId = aspnet_Membership.UserId
       LEFT OUTER JOIN CoreIdentitySample.dbo.AspNetUsers
                    ON aspnet_Membership.UserId = AspNetUsers.Id
WHERE  AspNetUsers.Id IS NULL

-- INSERT ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetRoles(Id, Name)
SELECT RoleId, RoleName
FROM aspnet_Roles;

-- INSERT USER ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetUserRoles(UserId, RoleId)
SELECT UserId, RoleId
FROM aspnet_UsersInRoles;

IF @@ERROR <> 0
  BEGIN
    ROLLBACK TRANSACTION MigrateUsersAndRoles
    RETURN
  END

COMMIT TRANSACTION MigrateUsersAndRoles

Após a conclusão do script anterior, o aplicativo da Identity do ASP.NET Core criado anteriormente é preenchido com Usuários associados. Os usuários precisam alterar suas senhas antes de fazer logon.

Observação

Se o sistema de Associação tiver usuários com nomes de usuário que não correspondem ao endereço de email, as alterações serão necessárias para o aplicativo criado anteriormente para acomodar isso. O modelo padrão espera que UserName e Email sejam os mesmos. Para situações em que são diferentes, o processo de logon precisa ser modificado para usar UserName em vez de Email.

No PageModel da Página de Logon, localizada em , remova o atributo [EmailAddress] da propriedade Email. Renomeie-o como UserName. Isso requer uma alteração onde quer que o EmailAddress seja mencionado, no Modo de Exibição e PageModel. O resultado se parece com o seguinte:

Logon Fixo

Próximas etapas

Neste tutorial, você aprendeu a portar usuários da associação do SQL para a Identity do ASP.NET Core 2.0. Para obter mais informações sobre a Identity do ASP.NET Core, consulte Introdução à Identity.