Por padrão, a maioria das plataformas de desenvolvimento web configuram as aplicações de modo a permitir que o usuário fique logado apenas por um determinado tempo e, após esse período, o mesmo é automaticamente desconectado. Todavia, existem alguns cenários em que precisamos exatamente do oposto: um login permanente que jamais desconecta, como em aplicações de e-mails, armazenamento de arquivos na nuvem, ou quando temos uma aplicação de intranet que exige essa funcionalidade. Ao ler este artigo, você conseguirá criar uma sessão permanente de login e poderá usar essa técnica em qualquer tipo de projeto .NET.

Passo 1: Formulários de autenticação

Num projeto web .NET, toda configuração que se refere à autenticação de formulário é feita no arquivo web.config, cujos valores padrão são apresentados na Listagem 1. É a partir dele que se torna possível criar um “login ilimitado” e configurar a autenticação de acordo com nossas necessidades.

Listagem 1. Configuração de autenticação de formulários

<authentication mode="Forms">
    <forms loginUrl="~/Account/LogOn" 
           protection="All" 
           timeout="1" 
           name=".CONTROLELOGINUSUARIO" 
           path="/" 
           requireSSL="false" 
           slidingExpiration="true" 
           defaultUrl="~/Home/Index" 
          cookieless="UseDeviceProfile" 
          enableCrossAppRedirects="false" /></authentication>
  • Linha 2: loginUrl aponta para a página de login personalizada do seu aplicativo. Você deve colocar a página de login em uma pasta SSL (Secure Sockets Layer). Isso ajuda a garantir a integridade das credenciais quando forem passadas no navegador para o servidor web;
  • Linha 3: protection é definida como "All" para especificar a privacidade e integridade do ticket de autenticação do formulário;
  • Linha 4: timeout é utilizado para especificar o tempo limite da sessão autenticada em minutos (o valor padrão é de 30 minutos);
  • Linhas 5 e 6: definem os valores do nome (este, de preferência, deve estar sempre em maiúsculo, para fins de padronização) e local do arquivo de configuração do aplicativo;
  • Linha 7: requireSSL é definido como "false". Isso significa que os cookies de autenticação podem ser transmitidos através de canais que não são criptografados por SSL. Se você está preocupado com hackers, deve considerar a opção "true";
  • Linha 10: definimos que para os perfis de usuário da aplicação ("UseDeviceProfile"), cookies serão usados pelo aplicativo nos navegadores para manter o controle de sessão. Caso um navegador que não suporta cookies acesse o site, não será permitida a autenticação na URL.

Passo 2: Modificando o tipo de autenticação

O único meio que dispomos de compartilhar uma sessão de usuário entre o navegador e o servidor de forma permanente, é através dos cookies. Em função disso, criaremos uma classe que ajudará com tal lógica interceptando a requisição de login que chegar à aplicação. Na Listagem 2 apresentamos o código da classe CookieHelper, cujo papel principal será configurar o cookie de autenticação permanente do usuário na aplicação. Como podemos notar, essa classe conta com um construtor que recebe o objeto HTTP response – que usaremos para modificar os dados dos cookies no fluxo do login – e o método principal SetLoginCookie(), que recebe três parâmetros: userName, password e isPermanentCookie, utilizado para ativar/desativar o recurso de login permanente.

Listagem 2. Criando um CookieHelper

public sealed class CookieHelper
{
    private HttpResponseBase _response;
 
    public CookieHelper(HttpResponseBase response)
    {
        _response = response;
    }
 
    public void SetLoginCookie(string userName, string password, bool isPermanentCookie)
    {
        if (_response != null)
        {
            if (isPermanentCookie)
            {
                FormsAuthenticationTicket userAuthTicket = new FormsAuthenticationTicket(
                           1, 
                           userName, 
                           DateTime.Now, 
                           DateTime.MaxValue, 
                           true, 
                           password, 
                           FormsAuthentication.FormsCookiePath);
 
                string encUserAuthTicket = FormsAuthentication.Encrypt(userAuthTicket);
                HttpCookie userAuthCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encUserAuthTicket);
                if (userAuthTicket.IsPersistent) userAuthCookie.Expires = userAuthTicket.Expiration;
 
                userAuthCookie.Path = FormsAuthentication.FormsCookiePath;
                _response.Cookies.Add(userAuthCookie);
            }
            else
            {
                FormsAuthentication.SetAuthCookie(userName, isPermanentCookie);
            }
        }
    }
}
  • Linha 14: avaliamos se o atributo isPermanentCookie é igual a true, ou seja, se o usuário deseja o login permanente. Caso seja passado o valor false, entramos no fluxo da linha 35;
  • Linha 16: criamos um objeto do tipo FormsAuthenticationTicket, que tem como principal responsabilidade guardar os valores e propriedades de identificação do usuário. Seu método construtor recebe sete atributos:
    • version: que recebe o número da versão do ticket. O desenvolvedor pode criar um versionamento linear de objetos helpers que serão salvos na sessão e usar o que melhor lhe convir;
    • name: recebe o nome do usuário associado ao ticket, ou seja, que estiver logado na sessão;
    • issueDate: recebe a data e hora em que o ticket foi criado;
    • expirationDate: recebe a data e hora em que o ticket irá expirar. O valor “DateTime.MaxValue” é uma constante que representa o maior valor possível de um DateTime (equivalente a 31 de dezembro de 9999, no calendário Gregoriano) na API de datas do .NET;
    • isPersistent: caso seja enviado o valor true, define que o ticket será salvo permanentemente (ao longo das sessões do navegador); caso contrário, o valor do atributo é ignorado;
    • userData: recebe dados específicos do usuário para serem armazenados junto ao cookie. No nosso caso, salvaremos apenas a senha;
    • cookiePath: recebe o caminho do ticket quando salvo em um cookie. Esse valor pode ser recuperado através do atributo FormsCookiePath da classe FormsAuthentication.
  • Linha 25: criptografamos em uma string os dados do ticket que criamos antes via método Encrypt(). Isso porque não podemos armazenar tais dados sem esse tratamento em cookies, cujas informações, por padrão, estão livremente acessíveis aos usuários pelo navegador;
  • Linha 26: criamos o objeto relacionado ao cookie HTTP (userAuthCookie) passando em seu construtor o nome do cookie usado para armazenar a autenticação do ticket (via propriedade FormsCookieName da classe FormsAuthentication) bem como a string criptografada definida na linha anterior;
  • Linha 27: inserimos a lógica para verificar se o ticket é persistente, ou seja, se os dados do login do usuário não devem expirar. Caso positivo, definimos a propriedade Expires do cookie HTTP recém-criado para o valor Expiration do ticket (esse valor se refere ao atributo expirationDate, que vimos há pouco na criação do objeto de ticket);
  • Linha 28: configuramos o caminho (Path) onde o cookie será salvo como o default da API de autenticação do ASP.NET (esse valor pode ser recuperado via propriedade FormsCookiePath do objeto FormsAuthentication);
  • Linha 29: adicionamos o objeto userAuthCookie recém-criado e configurado à lista de cookies do response;
  • Linha 34: Através do método SetAuthCookie(), definimos que o usuário não terá sessão permanente na aplicação, resetando seu estado para o default.

Passo 3: Concluindo a autenticação permanente

Para testar a implementação, a Listagem 3 traz um método de exemplo que pode ser chamado após o clique do botão login. Uma vez que o login do usuário tenha sido efetuado com sucesso, criamos um objeto do tipo CookieHelper (linha 4) e chamamos seu método SetLoginCookie() passando os dados do usuário, a senha e o booleano de sessão permanente. Dessa forma, a API do .NET removerá o timeout padrão e, a cada nova requisição, verificará se os dados de autenticação se encontram no cookie autorizando, assim, o usuário.

Listagem 3. Validação do login do usuário

private bool Login(string username, string password, bool rememberMe) {
    bool isUserValid = // Lógica específica para verificar login do usuário
    if (isUserValid) { // Se o usuário autenticar com sucesso
        CookieHelper newCookieHelper = new CookieHelper(HttpContext.Response);
        newCookieHelper.SetLoginCookie(userName, password, rememberMe);
        return true;
    } else {
        return false;
    }
}