Neste artigo iremos demonstrar o uso de uma ferramenta de segurança que já vem integrada ao .NET Framework desde a versão 2.0: o Membership. É uma classe que não pode ser herdada e que é responsável por efetuar a segurança de aplicações. Ela valida as credenciais dos usuários, gerencia as configurações da aplicação, regras de acesso e muito mais. Pode ser utilizado sozinho ou em conjunto com o FormsAuthentication para efetuar a segurança de um aplicativo web ou site.

Através do provedor Membership podemos criar novos usuários e senhas em um banco de dados e validar a identidade do usuário usando a informação armazenada. Para armazenar a informação o provedor usa um banco de dados SQL Server que é gerado automaticamente, e, de forma transparente na pasta App_Data, se usarmos o modo padrão do provedor.

Podemos também utilizar a ferramenta aspnet_regsql.exe, que vem com a ASP .NET, para configurar o armazenamento de dados, ou seja adicionar a estrutura das tabelas do Membership na nossa própria base de dados.

O que o Membership fornece:

  • Criação e Manutenção de usuários;
  • Armazenamento de informações de usuários (Nomes, senhas, endereços de email e etc.) em uma base Microsoft SQL Server;
  • Autenticação de Usuários;
  • Gerenciamento de senhas (Criação, alteração, recuperação e redefinição);
  • Controle de Acesso a determinadas páginas e módulos das nossas aplicações;
  • Criação de Regras de Acesso;

Ele depende de provedores de associação para se comunicar com a base de dados e no .NET Framework inclui uma SqlMembershipProvider que armazena informações do usuário. Após efetuar a configuração do Membership, tabelas serão criadas na sua base SQL Server a fim de gerir os dados de segurança da aplicação. Com relação às senhas, as mesmas são gravadas já criptografadas.

O Membership contém a ferramenta própria Web Site Administration (WAT), responsável para efetuar toda a administração e configuração da sua aplicação. Para acessar esta basta, após a criação do seu projeto e a configuração do Membership, acessar o no Visual Studio o menu WebSite -> ASP. NET Configuration. Lá teremos as seguintes opções:

  • Security – Configuração de usuários e permissões de acesso ao sistema. Dentro dessa opção temos também como criar as Roles (Regras), ou seja, as regras de acesso da nossa aplicação como, por exemplo, negar que usuários anônimos tenham acesso a determinadas pasta e arquivos.
  • Apllication Configuration – Gerencia a configuração e definições do sistema;
  • Provider Configuration – Gerencia a forma como armazenar os dados de administração do seu sistema.

É altamente recomendado a utilização de senhas criptografadas na configuração do provider, bastando alterar o atributo passwordFormat para Hashed ou Encrypted, onde Hashed é o formato mais seguro. Os valores da chave de criptografia dos algoritmos são armazenados no machinekey.

Antes de tudo você precisará definir algumas coisas como:

  • Qual provedor de associação utilizar?
  • Qual forma de criptografia utilizar?
  • Qual forma de autenticação utilizar?

Após isso deverá configurar o membership de acordo com a sua necessidade.

Na Listagem 1 temos um exemplo de configuração que a WebConfig da nossa aplicação deverá ter após a configuração do Membership na aplicação, de acordo com a necessidade.

Listagem 1. Configuração que deverá constar no Webonfig


  <configuration>
  <connectionStrings>
    <add name="MySqlConnection" connectionString="Data 
      Source=MySqlServer;Initial Catalog=aspnetdb;Integrated
      Security=SSPI;" />
  </connectionStrings>
  <system.web>
    <authentication mode="Forms" >
      <forms loginUrl="login.aspx"
        name=".ASPXFORMSAUTH" />
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
    <membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
      <providers>
        <clear />
        <add 
          name="SqlProvider" 
          type="System.Web.Security.SqlMembershipProvider" 
          connectionStringName="MySqlConnection"
          applicationName="MyApplication"
          enablePasswordRetrieval="false"
          enablePasswordReset="true"
          requiresQuestionAndAnswer="true"
          requiresUniqueEmail="true"
          passwordFormat="Hashed" />
      </providers>
    </membership>
  </system.web>
</configuration>

Confira a seguir alguns métodos do Membership:

  • CreateUser(String, String): Adiciona um novo usuário a sua aplicação;
  • CreateUser (String, String, String): Adiciona um novo usuário com um endereço de e-mail especificado a sua aplicação;
  • DeleteUser(String): Exclui um usuário da aplicação e os seus dados relacionados na base de dados;
  • FindUsersByEmail(String): Retorna um conjunto de usuários que tenham associação com o email passado como parâmetro;
  • GeneratePassword: Gera uma senha aleatória do comprimento especificado;
  • GetAllUsers(): Retorna uma coleção com todos os usuários do banco de dados;
  • ValidateUser: Verifica se nome do usuário e senha são válidos;
  • GetUserNameByEmail: Retorna o nome do usuário a partir do e-mail informado;
  • GetUser (String): Retorna informações do usuário informado;

Propriedades do Membership:

  • ApplicationName: Obtém ou define o nome da aplicação;
  • EnablePasswordReset: Obtém um valor que indica se o provedor de associação está configurado para que os usuários possam redefinir sua senha;
  • MinRequiredPasswordLength: Obtém o comprimento mínimo para as senhas;
  • RequiresQuestionAndAnswer: Obtém um valor que indica se o provedor de associação exige que o usuário responda a uma pergunta secreta para poder redefinir a senha;
  • UserIsOnlineTimeWindow: Obtém o dia e horário do último login do usuário;

NOTA: A maioria das funcionalidades que o Membership fornece são encapsuladas em vários controles Web de gerenciamento de usuário.

O Membership também fornece alguns controles que podem realizar várias funções dentro da nossa aplicação. Se vocês analisarem na paleta de componentes do Visual Studio teremos uma aba chamada Login, onde temos diversos componentes que integram diretamente com o Membership. A seguir temos um breve resumo sobre o que cada um deles é capaz de efetuar.

  • Login: Componente que gera um controle HTML responsável por efetuar toda a validação de login do usuário. Algo muito legal deste componente é que após o usuário tentar efetuar o login diversas vezes e não conseguir efetuar logon, o seu usuário será bloqueado automaticamente.
  • CreateUserWizard: Componente que gera um controle HTML com o conteúdo necessário para a criação de um novo usuário. Esse componente pode ser configurado de diversas formas como por exemplo adicionando a opção de pergunta secreta;
  • ChangePassword: Componente que gera um controle HTML com conteúdo necessário para efetuar a alteração de senha dos usuários existentes na base de dados;
  • PasswordRecovery: Componente que gera um controle HTML capaz de efetuar a recuperação de senha via e-mail para os usuários;
  • LoginView: Componente que gera um controle HTML com um conteúdo responsável por mostrar o status do usuário se está logado ou não;
  • LoginName: Componente que gera um controle HTML responsável por exibir o nome do usuário logado.

Esses controles funcionam em conjunto com os provedores Membership e Roles. Eles não necessitam de nenhuma linha de código para funcionarem perfeitamente, pois eles já vêm com um template definido, mas podem ser alterados bastando utilizar a opção “converter to template” e ajustar de acordo com o seu gosto.

Na Listagem 2 demonstraremos o HTML que é gerado pelo componente ChangePassword (Responsável por efetuar a alteração de senha para o usuário) e após a conversão para template. Para esse e os demais exemplos a seguir foram utilizados o Visual Studio 2010 com o .NET Framework 4.0 e a classe Membership.

Listagem 2. Código HTML ChangePassword


  <asp:ChangePassword ID="ChangePassword1" runat="server" ChangePasswordFailureText="Senha(s) inválida(s)!"
                  Width="300px">
                  <CancelButtonStyle />
                  <ChangePasswordButtonStyle />
                  <ChangePasswordTemplate>
                      <table class="form-inline">
                          <tr>
                              <td>
                                  <table>
                                      <tr>
                                          <td align="right">
                                              <asp:Label ID="CurrentPasswordLabel" runat="server" AssociatedControlID="CurrentPassword">Senha:</asp:Label>
                                          </td>
                                          <td>
                                              <asp:TextBox ID="CurrentPassword" runat="server" Height="30px" MaxLength="10" required=""
                                                  TextMode="Password"></asp:TextBox>
                                              <br />
                                          </td>
                                      </tr>
                                      <tr>
                                          <td align="right">
                                              <br />
                                              <asp:Label ID="NewPasswordLabel" runat="server" AssociatedControlID="NewPassword">Nova Senha:</asp:Label>
                                          </td>
                                          <td>
                                              <asp:TextBox ID="NewPassword" runat="server" Height="30px" MaxLength="10" required=""
                                                  TextMode="Password"></asp:TextBox>
                                          </td>
                                      </tr>
                                      <tr>
                                          <td align="right">
                                              <br />
                                              <asp:Label ID="ConfirmNewPasswordLabel" runat="server" AssociatedControlID="ConfirmNewPassword">Confirmação:</asp:Label>
                                          </td>
                                          <td>
                                              <asp:TextBox ID="ConfirmNewPassword" runat="server" Height="30px" MaxLength="10"
                                                  required="" TextMode="Password"></asp:TextBox>
                                          </td>
                                      </tr>
                                      <tr>
                                          <td align="center" colspan="2">
                                              <asp:CompareValidator ID="NewPasswordCompare" runat="server" ControlToCompare="NewPassword"
                                                  ControlToValidate="ConfirmNewPassword" Display="Dynamic" ErrorMessage="As senhas informadas não conferem!"
                                                  ValidationGroup="ChangePassword1"></asp:CompareValidator>
                                          </td>
                                      </tr>
                                      <tr>
                                          <td align="center" colspan="2" style="color: Red;">
                                              <asp:Literal ID="FailureText" runat="server" EnableViewState="False"></asp:Literal>
                                          </td>
                                      </tr>
                                      <div class="legenda">
                                          <tr>
                                              <td align="center" colspan="2">
                                                  <br />
                                              </td>
                                          </tr>
                                      </div>
                                  </table>
                                  <div align="right">
                                      <asp:Button ID="ChangePasswordPushButton" runat="server" CommandName="ChangePassword"
                                          Text="Confirmar" ValidationGroup="ChangePassword1" Width="80px" />
                                      <asp:Button ID="btnCancelar" runat="server" formnovalidate="formnovalidate" OnClick="btnCancelar_Click"
                                          Text="Cancelar" Width="80px" />
                                      <br />
                                  </div>
                              </td>
                          </tr>
                      </table>
                  </ChangePasswordTemplate>
                  <ContinueButtonStyle />
                  <InstructionTextStyle />
                  <PasswordHintStyle />
                  <SuccessTemplate>
                      <table>
                          <tr>
                              <td>
                                  <table>
                                      <tr>
                                          <td align="center">
                                              <h2>
                                                  Alteração Senha</h2>
                                          </td>
                                      </tr>
                                      <tr>
                                          <td>
                                              <h5>
                                                  Senha alterada com sucesso!</h5>
                                          </td>
                                      </tr>
                                      <tr>
                                          <td align="right">
                                              <asp:Button ID="ContinuePushButton" runat="server" BorderStyle="Solid" BorderWidth="1px"
                                                  CausesValidation="False" CommandName="Continue" Text="Continuar" />
                                          </td>
                                      </tr>
                                  </table>
                              </td>
                          </tr>
                      </table>
                  </SuccessTemplate>
                  <TextBoxStyle />
                  <TitleTextStyle />
              </asp:ChangePassword>      

Listagem 3. PasswordRecovery código HTML


  <asp:PasswordRecovery ID="PasswordRecovery1" runat="server" 
          AnswerLabelText="Resposta:" AnswerRequiredErrorMessage="Resposta requerida." 
          GeneralFailureText="Sua tentativa para recuperar sua senha não teve êxito. Por favor tente novamente." 
          QuestionFailureText="Sua resposta não pôde ser verificada. Por favor tente novamente." 
          QuestionInstructionText="Responda a pergunta seguinte para receber sua senha." 
          QuestionLabelText="Pergunta:" QuestionTitleText="Identificação confirmada" 
          SubmitButtonText="Enviar" SuccessText="A sua solicitação de senha foi realizada com sucesso." 
          UserNameFailureText="Nós não conseguimos localizar sua informação. Por favor tente novamente." 
          UserNameInstructionText="Entre com seu Nome de Usuário para receber sua senha." 
          UserNameLabelText="Usuário:" UserNameRequiredErrorMessage="Usuário requerido." 
          UserNameTitleText="Esqueceu sua senha?" 
              onsendingmail="PasswordRecovery1_SendingMail">
          <SubmitButtonStyle BorderWidth="1px" Width="60px" />
      </asp:PasswordRecovery>

Podemos verificar na Listagem 3 que se caso não convertemos em template, deixando o componente com uma das opções de layout padrão, o código HTML fica bem mais simplificado. Com o nosso componente podemos utilizar alguns eventos já predefinidos nele como, por exemplo, o evento SendingMail, que é disparado após a recuperação da senha, onde poderemos enviar um e-mail para o usuário com os dados de acesso dele.

Na Listagem 4 temos o código responsável por obter as informações do usuário e encaminhar o email.

Listagem 4. PasswordRecovery recuperando dados e enviando email


  protected void PasswordRecovery1_SendingMail(object sender, MailMessageEventArgs e)
          {
              e.Cancel = true;
              string msg = string.Empty;
              // Obtém os dados do usuário
              MembershipUser user = Membership.GetUser(PasswordRecovery1.UserName, false);
              // Altera a senha do usuário para a senha “novasenha”
              user.ChangePassword(user.ResetPassword(), "novasenha");
              // Grava as informações de alteração na base
              Membership.UpdateUser(user);
              // Montagem do email
              string corpo = string.Format(@"<html>
      <head>
          <title>Recuperação de Senha</title>
      </head>
          <body>
          <b>Portal de Serviços</b><br />
          <p>Recebemos a sua solicitação de recuperação de senha.</p>
          <p>Usuário:  <b>{0}</b><br />Senha:  <b>novasenha</b></p>
          <p>Altere a sua senha o mais breve possível!</p>
          <br />
          <br />        
          </body>
  </html>
  ", PasswordRecovery1.UserName);
  // Envia o email de recuperação. 
              if (EnviarEmail(user.Email, "Solicitação de senha", corpo, string.Empty))
                  msg = " Email Enviado com sucesso";
              else
                  msg = " Não foi possivel enviar o email tente novamente";
   
              if (!string.IsNullOrEmpty(msg))
                  ScriptManager.RegisterStartupScript(Page, Page.GetType(), "Alert", string.Format("alert('{0}'); window.location.href='Index.aspx';", msg), true);
          }

Com esse código conseguimos efetuar a recuperação de senha para o determinado usuário e enviá-la por email de forma simples utilizando apenas um evento do componente PasswordRecovery.

NOTA: Esse método EnviarEmail deve funcionar de forma diferente do método já existente em sua aplicação responsável pelo envio de email.

Listagem 5. Verificando se o usuário está bloqueado


  protected void btnValidarUser_Click(object sender, EventArgs e)
  {
     // Obtendo os dados do usuário a partir do seu username
      MembershipUser usuario = Membership.GetUser("nome usuário");
     // Verificando o status do usuário a partir da sua propriedade isApproved
      if(usuario!= null && ! usuario.isApproved) {
          ClientScript.RegisterStartupScript(
             this.GetType(), "alert", "alert('Usuário com status bloqueado.');", true);
      }    
  }

Com a Listagem 5 conseguimos verificar se determinado usuário encontra-se bloqueado ou não.

Com a chegada do Visual Studio 2013, a Microsoft adicionou um framework para gerenciar a identidade dos usuários chamado ASP.NET Identity. Esse veio para resolver algumas restrições e complicações que as versões anteriores do Membership possuíam.

Listagem 6. Direcionando Usuários para determinadas páginas após login


          protected void LoginPrincipal_LoggedIn(object sender, EventArgs e)
          {
     // Obtendo os dados do usuário a partir do seu username
              MembershipUser usuario = Membership.GetUser("nome usuário");
              if (usuario.Email.Contains("direcao"))
                  Response.Redirect("~/WorkspaceDirecao.aspx");
              else if (usuario.Email.Contains("financeiro"))
                  Response.Redirect("~/WorkspaceFinanceiro.aspx");
              else if (usuario.Email.Contains("comercial"))
                  Response.Redirect("~/WorkspaceFinanceiro.aspx");
              else Response.Redirect("~/Index.aspx");
         }

Na Listagem 6 conseguimos fazer com que cada usuário, após o login, seja direcionado para uma tela específica. Como exemplo utilizamos o email do usuário para que, caso ele faça parte da direção, será direcionado para uma determinada tela, e assim por diante. Foi utilizado o evento LoggedIn do nosso componente Login, que é disparado após o usuário efetuar o login e conseguir permissão para acessa a aplicação.

O Membership é bom, mas têm algumas limitações encontradas:

  • Foi desenvolvido para utilizar o schema do SQL Server e infelizmente não é possível alterá-lo;
  • Foi projetado “apenas” para uso sobre banco de dados relacional;
  • Impossível utilizar especificações do OWIN;
  • Difícil acesso a informações do perfil do usuário;
  • Nomenclaturas das tabelas são de difícil entendimento;

Até o momento, a tecnologia mais utilizada e indicada pela própria Microsoft é o Membership, apesar do ASP.NET Identity ter um futuro promissor, quem sabe futuramente possa ser o carro chefe em termos de segurança das aplicações. Porém, existem outras opções ainda, como o OWIN e Katana.

O Membership foi desenvolvido para uso de aplicações Web, embora possa ser empregados sem dificuldades em outros tipos de aplicações .NET. Podemos concluir que o uso do Membership simplifica consideravelmente o desenvolvimento de aplicações mais seguras, pois não é necessário reinventar a roda como, por exemplo, criar um Login todo do zero. Essa é uma ferramenta muito importante, que contém um leque muito maior de funcionalidades.

Espero que tenham gostado do assunto.

Links

Introdução ao Membership
https://msdn.microsoft.com/pt-br/library/system.web.security.membership%28v=vs.110%29.aspx

Membership Class
https://msdn.microsoft.com/pt-br/library/system.web.security.membership%28v=vs.110%29.aspx

ASP .NET identity
http://www.asp.net/identity

Criptografia Hash
http://www.gta.ufrj.br/grad/07_1/ass-dig/TiposdeCriptografia.html

OWIN
http://www.asp.net/aspnet/overview/owin-and-katana