André Alves de Lima

Talking about Software Development and more…

Implementando uma janela de login com Windows Forms ou WPF

Praticamente todo aplicativo comercial que desenvolvemos possui algum tipo de autenticação. Uma opção que temos ao programarmos sistemas desktop com o .NET Framework (Windows Forms ou WPF) é elaborarmos do zero toda a estrutura de login. Criamos as tabelas no banco de dados e uma maneira de criptografarmos a senha de forma que ela fique segura no banco de dados. Porém, que tal utilizarmos algo que já esteja pronto no próprio .NET Framework ao invés de criarmos tudo do zero? É justamente esse o tema do artigo de hoje: como implementarmos uma janela de login utilizando o ASP.NET Identity em aplicativos Windows Forms ou WPF.

Quando tive a ideia de escrever esse artigo, pensei em mostrar como utilizar o ASP.NET Membership Provider em aplicações desktop. Até encontrei um exemplo de sua implementação no site de outro MVP. Porém, como o ASP.NET Membership Provider é um tanto quanto antigo, resolvi procurar algo mais novo. Como o ASP.NET Identity é o sucessor do ASP.NET Membership Provider, pensei em pesquisar uma maneira de utilizá-lo em aplicações desktop. E acabou que essa é uma tarefa extremamente simples.

Como funciona o login no ASP.NET?

Se você não conhece o ASP.NET Identity, ele é basicamente a estrutura responsável pela autenticação em projetos web criados com o Visual Studio 2013 ou superior. Se você nunca criou um projeto do tipo ASP.NET MVC com o Visual Studio 2013, experimente criar para ver o resultado. Na janela de criação de projetos web no Visual Studio 2013, note que é possível escolhermos alguns tipos de autenticação através do botão “Change Authentication“:

A opção “Individual User Accounts” corresponde justamente à utilização do ASP.NET Identity. Ao escolhe-lo, o projeto web contará automaticamente com os botões “Register” e “Log in“, que podem ser utilizados para gerenciar o registro e autenticação de usuários:

Como o ASP.NET utiliza por padrão o Entity Framework (apontando para uma instância de localdb), quando o registro ou autenticação de usuários é utilizado pela primeira vez, o Entity Framework cria no localdb as tabelas de usuários, roles e demais tabelas relacionadas à autenticação:

Agora fica a pergunta: como conseguimos utilizar em aplicativos desktop toda essa estrutura de login que funciona por trás dos panos no ASP.NET Identity? É isso que veremos nas próximas seções.

Utilizando o ASP.NET Identity no Windows Forms

Como você deve ter acompanhado, grande parte do .NET Framework está sendo transformado em open source. O time do ASP.NET já seguia essa trilha do open source há mais tempo e, como o ASP.NET Identity faz parte desse pacote, ele está disponível no CodePlex. Não só isso, mas, a Microsoft também disponibilizou pacotes do NuGet para conseguirmos adicionar o ASP.NET Identity em qualquer tipo de projeto que estivermos desenvolvendo, o que deixa o nosso trabalho muito mais fácil.

Para utilizarmos o ASP.NET Identity no Windows Forms, primeiramente vamos criar um projeto do tipo “Windows Forms Application“. Feito isso, adicione o pacote do NuGet “Microsoft ASP.NET Identity EntityFramework” (caso tenha dúvidas sobre esse passo, confira este artigo sobre como adicionar pacotes do NuGet utilizando o Visual Studio). Com isso, o Visual Studio referenciará todos os assemblies necessários ao ASP.NET Identity + Entity Framework:

Agora que já temos as referências necessárias, vamos trabalhar no design do formulário. Adicione dois Labels, dois TextBoxes e dois Buttons no formulário de forma que ele fique parecido com a imagem abaixo:

Os TextBoxes foram chamados de “usuarioTextBox” e “senhaTextBox” (esse último com a propriedade PasswordChar configurada para “*“). Os botões receberam o nome de “loginButton” e “criarUsuarioButton“.

Com o layout do formulário desenhado, podemos alternar ao code-behind para configurarmos a lógica dos dois botões. A primeira tarefa a ser realizada é adicionarmos duas cláusulas “using” na declaração do nosso formulário, isso porque os namespaces do ASP.NET Identity são muito longos e o código ficaria muito poluído se não adicionássemos estes “usings“:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;

Feito isso, temos que criar uma instância de UserManager dentro do nosso formulário. Essa é a classe responsável por fazer a autenticação, criar novos usuários e demais tarefas relacionadas ao gerenciamento do login. O construtor de UserManager é genérico, devendo receber o tipo de “IdentityUser” a ser utilizado. No nosso caso, como utilizaremos o provider do Entity Framework, temos que utilizar a classe IdentityUser presente dentro do namespace Microsoft.AspNet.Identity.EntityFramework. O construtor também recebe um parâmetro, que deve ser algum tipo que herde de IUserStore. No nosso caso, utilizaremos a UserStore do Entity Framework. Veja como fica a inicialização do UserManager:

    public partial class FormLogin : Form
    {
        UserManager<IdentityUser> _userManager;

        public FormLogin()
        {
            InitializeComponent();

            _userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(new IdentityDbContext()));
        }
    }

Finalmente, vamos implementar o código dos botões “Login” e “Criar novo usuário“. Veja o resultado no trecho de código abaixo:

        private void loginButton_Click(object sender, EventArgs e)
        {
            var user = _userManager.Find(usuarioTextBox.Text, senhaTextBox.Text);

            if (user != null)
            {
                MessageBox.Show("Usuário autenticado!");
            }
            else
            {
                MessageBox.Show("Usuário e/ou senha inválidos!");
            }
        }

        private void criarUsuarioButton_Click(object sender, EventArgs e)
        {
            var result = _userManager.Create(new IdentityUser(usuarioTextBox.Text), senhaTextBox.Text);

            if (result.Succeeded)
            {
                MessageBox.Show("Usuário criado com sucesso!");
            }
            else
            {
                MessageBox.Show(string.Format("Erro criando usuário:\n{0}", string.Join("\n", result.Errors)));
            }
        }

Note que, para validarmos o usuário e senha (botão “Login“), utilizamos o método Find do UserManager. Esse método tem duas opções de retorno: ao retornar uma instância do usuário, significa que o usuário e senha existem no banco de dados; ao retornar null, significa que a combinação de usuário e senha não existem no banco de dados (podendo significar que o usuário não existe ou que a senha está incorreta).

Já na parte de criação de usuários, utilizamos o método Create da classe UserManager. Esse método deve receber uma nova instância de IdentityUser (criada passando o “id” do usuário) e a senha que deve ser utilizada para o usuário que está sendo criado. O método “Create” retorna uma instância de IdentityResult. Com essa instância de IdentityResult retornada pelo método “Create“, conseguimos detectar se a criação do usuário foi efetivada com sucesso através da propriedade Succeeded. Caso ela seja verdadeira, a criação foi bem-sucedida. Caso contrário, o conjunto de erros é retornado através da propriedade Errors.

Ao tentarmos criar um usuário que já existe, o ASP.NET Identity retorna um erro:

Além disso, por padrão, o ASP.NET Identity só permite a criação de usuário que tenham uma senha de, no mínimo, seis caracteres:

O ASP.NET Identity gerencia as senhas utilizando hashes, de maneira que as senhas são armazenadas de forma irreconhecível no banco de dados:

Para configurarmos o banco de dados utilizado pelo ASP.NET Identity, basta adicionarmos uma connection string no nosso arquivo app.config. Se não configurarmos nenhuma connection string no app.config, o Entity Framework criará um banco de dados chamado “DefaultConnection” na instância default do SQL Server instalado na máquina (esse é o comportamento padrão do Entity Framework Code-First):

Podemos facilmente configurar outro banco de dados adicionando a connection string com o nome de “DefaultConnection” no nosso app.config:

  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=.\sqlexpress;Initial Catalog=identity;Integrated Security=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

E com isso temos o básico do básico da utilização do ASP.NET Identity em aplicações Windows Forms. Agora vamos ver como conseguimos implementar a mesma funcionalidade em WPF.

Utilizando o ASP.NET Identity no WPF

Para a utilização do ASP.NET Identity no WPF, não vou detalhar todos os passos e explicações que fiz na seção de Windows Forms. Por favor, leia primeiro a implementação no Windows Forms para entender exatamente como o código funciona por trás dos panos.

Primeiramente, temos que criar um novo projeto do tipo “WPF Application“. Adicione as referências ao ASP.NET Identity utilizando o NuGet, como explicado na seção de Windows Forms.

Feito isso, configure o Grid da Window da seguinte maneira:

    <Grid>
        <StackPanel>
            <TextBlock Text="Usuário" />
            <TextBox x:Name="UsuarioTextBox" />
            <TextBlock Text="Senha" />
            <TextBox x:Name="SenhaTextBox" />
            <Button x:Name="LoginButton"
                    Margin="1"
                    Content="Login"
                    Click="LoginButton_Click" />
            <Button x:Name="CriarUsuarioButton" 
                    Margin="1"
                    Content="Criar usuário"
                    Click="CriarUsuarioButton_Click"/>
        </StackPanel>
    </Grid>

Em seguida, vá até o code-behind da Window e adicione os “usings” para os namespaces do ASP.NET Identity:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;

Finalmente, ajuste o restante do código da Window da seguinte maneira:

    public partial class MainWindow : Window
    {
        UserManager<IdentityUser> _userManager;

        public MainWindow()
        {
            InitializeComponent();

            _userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(new IdentityDbContext()));
        }

        private void LoginButton_Click(object sender, RoutedEventArgs e)
        {
            var user = _userManager.Find(UsuarioTextBox.Text, SenhaTextBox.Text);

            if (user != null)
            {
                MessageBox.Show("Usuário autenticado!");
            }
            else
            {
                MessageBox.Show("Usuário e/ou senha inválidos!");
            }
        }

        private void CriarUsuarioButton_Click(object sender, RoutedEventArgs e)
        {
            var result = _userManager.Create(new IdentityUser(UsuarioTextBox.Text), SenhaTextBox.Text);

            if (result.Succeeded)
            {
                MessageBox.Show("Usuário criado com sucesso!");
            }
            else
            {
                MessageBox.Show(string.Format("Erro criando usuário:\n{0}", string.Join("\n", result.Errors)));
            }
        }
    }

E com isso, temos a mesma funcionalidade implementada também no WPF. Fácil, não?

Concluindo

A criação de janelas de login é quase que unânime em aplicações de negócios. Normalmente costumamos criar nossas tabelas para gerenciarmos os usuários, perfis, etc. Mas, por que ao invés de reinventarmos a roda, nós não utilizamos uma estrutura que já está pronta? Essa é a sugestão que eu faço neste artigo: vamos parar de implementar sistemáticas de login customizadas e vamos começar a utilizar o ASP.NET Identity, que já está pronto e atende bem às necessidades de login das aplicações de negócios.

Para deixar bem claro, o ASP.NET Identity vai muito além da simples criação e autenticação de usuários. Ele suporta perfis (“roles“), validação de e-mails utilizando tokens, two-factor authentication, etc, etc. Para ter uma ideia geral das outras funcionalidades do ASP.NET Identity que você pode integrar nas suas aplicações, confira este outro artigo.

E, finalmente, convido você a assinar a minha newsletter. Dessa forma, você fica por dentro das novidades do meu site, além de receber dicas extras que eu só compartilho por e-mail. Assine aqui ou utilizando o formulário abaixo.

Até a próxima!

André Lima

Image by marc falardeau used under Creative Commons
https://www.flickr.com/photos/49889874@N05/6101434856/

Newsletter do André Lima

* indicates required



Powered by MailChimp

10 thoughts on “Implementando uma janela de login com Windows Forms ou WPF

  • Boa tarde André!

    Para Utilizar o ASP.NET Identity no Windows Forms com MySQL, tenho que criar as mesmas tabelas na BD?

    Sinceramente nao sei como lhe agradecer por esses artigos fantásticos!

    Abraço!

    • andrealveslima disse:

      Olá Flávio, muito obrigado por mais esse comentário! Fico feliz que você esteja gostando dos artigos.. :)

      Quanto à sua questão, teoricamente deveria funcionar.. Como o EF tem o provider para o MySQL, uma vez tendo as tabelas no banco com a estrutura correta, é para funcionar.. Você chegou a tentar? Tenta aí e depois fala pra gente se funcionou..

      Abraço!
      André Lima

  • Felipe Lima disse:

    Olá André, implementei no meu pequeno projeto o que aprendi no seu tutorial, ajudou demais com o sistema de login do meu programa mas uma dúvida me surgiu, como eu faço a remoção de algum usuário que não seja mais necessário? Vasculhando por aí consegui remover ao utilizar o _userManager.Delete(user) porém só funciona caso eu tenha login e senha do usuário cadastrado. Você conhece uma forma de remover este usuário apenas pelo login? Desde já agradeço a atenção!

    • andrealveslima disse:

      Olá Felipe!

      Não entendi a relação entre a chamada de Delete e o fato de você precisar do login e senha.. Uma vez tendo a instância de user, você pode chamar o método Delete e o usuário será deletado, não? Em que parte você precisaria da senha nesse caso? Você poderia passar o código completo que você está utilizando na deleção?

      Abraço!
      André Lima

  • Camila disse:

    Olá André, muito interessante a solução. Me tira um dúvida (se puder). Eu já tenho a tabela de usuarios implementada, eu posso utilizar essa tabela já criada ou precisa seguir esse padrão que está no exemplo?

    • andrealveslima disse:

      Olá Camila!

      A parte visual (janela, botões, etc) você pode utilizar 100% a mesma ideia.. Só que a parte de infraestrutura, você até poderia ajustar o ASP.NET Identity para utilizar tabelas customizadas.. Mas, seria tanto ajuste que, nesse caso, eu acho que seria mais fácil você implementar a sue própria lógica de login mesmo.. Faz mais sentido utilizar o ASP.NET Identity com a estrutura padrão dele mesmo..

      Tem um tutorial para customizar a estrutura do ASP.NET Identity neste link:

      Overview of Custom Storage Providers for ASP.NET Identity

      Como você pode perceber, apesar de ser possível, dá um certo trabalho..

      Abraço!
      André Lima

  • Nill Frank disse:

    Dear Andre
    Beautiful guide, eh searched for a lot this type of guide, now I have a question about how I can adminstrate the menus (enable), actions enable buttons in each form (create, update, delete), I must clearly call the identity methods, but as I do for the administration of the whole menu, as does identity in mvc or web forms

    • andrealveslima disse:

      Hi Nill, thank you very much for commenting here.. I’m happy that this guide helped you somehow..

      Now, about your question.. As far as I know, unfortunately, for Windows Forms and WPF there isn’t any native way of doing that (as you have directly in ASP.NET).. You will need to read the permissions that the user has and enable/disable the menus accordingly..

      If you find something ready regarding this, let us know here, OK?

      Thanks again. Regards,
      André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *