Controle de Usuários para Sistemas ASP.NET - Parte 3 - Classe de Usuários
Leonel Fraga de Oliveira 30/08/2008 10:28

Olá pessoal! Enfim vamos a terceira parte do projeto de controle de usuários para aplicações ASP.NET. Nas partes anteriores, modelamos a base de dados e escrevemos a classe que manipula os perfis de usuário. Nesta parte iremos abordar a classe responsável pelas tarefas relativas ao usuário: inserir, atualizar, excluir e autenticar. Vamos ao código, lembrando sempre que o exemplo completo pode ser pego na Página de Suporteno link de download no final desta postagem.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Collections;
using System.Security.Cryptography;
 
namespace UserManager
{
    public class TUsuario Conexao
    {
        #region Parte VO
        private int _USUARIO_ID = 0;
        private string _NOME = "";
        private string _LOGIN = "";
        private int _PERFIL_ID = 0;
        private string _SENHA = ""; //a ser criptografada quando for gravada no BD
        private string _STATUS = "";
        private string _SqlSearch = "";
        private TPerfilUsuario _PERFIL = new TPerfilUsuario();
 
        public int UsuarioId { get { return _USUARIO_ID; } set { _USUARIO_ID = value; } }
        public string Nome { get { return _NOME; } set { _NOME = value; } }
        public string Login { get { return _LOGIN; } set { _LOGIN = value; } }
        public int PerfilId { get { return _PERFIL_ID; } set { _PERFIL_ID = value; } }
        public string Senha { get { return _SENHA; } set { _SENHA = value; } }
        public string Status { get { return _STATUS; } set { _STATUS = value; } }
        public TPerfilUsuario Perfil { get { _PERFIL.PerfilId = _PERFIL_ID; _PERFIL.SetByID(); return _PERFIL; } }
        #endregion
 
        #region Parte DAO
        public void SetByID()
        {
            try
            {
                this._SelectSQL = "select * from USUARIOS where USUARIO_ID = @USUARIO_ID ";
                ClearSQLParams();
                AddSQLParam("@USUARIO_ID", this._USUARIO_ID, ParameterDirection.Input);
 
                this.Select(true);
                if (this.ListaCamposTabela.Count > 0)
                {
                    this._USUARIO_ID = Consts.Funcoes.NullOrInt(this["USUARIO_ID"]);
                    this._NOME = Consts.Funcoes.NullOrString(this["NOME"]);
                    this._LOGIN = Consts.Funcoes.NullOrString(this["LOGIN"]);
                    this._PERFIL_ID = Consts.Funcoes.NullOrInt(this["PERFIL_ID"]);
                    this._STATUS = Consts.Funcoes.NullOrString(this["STATUS"]);
                    this._PERFIL.PerfilId = _PERFIL_ID;
                    this._PERFIL.SetByID();
                }
                else
                {
                    this.fMsgInfo = "Registro não encontrado";
                }
            }
            catch (Exception ex)
            {
                this.fMsgInfo = "Erro ao obter dados -> " + ex.Message;
            }
        }
 
        public bool Inserir()
        {
            bool st = false;
            ClearSQLParams();
            AddSQLParam("@USUARIO_ID", Consts.Funcoes.ZeroOrDBNull(this._USUARIO_ID), ParameterDirection.Output);
            AddSQLParam("@NOME", Consts.Funcoes.ValueOrDBNull(this._NOME.ToUpper().Trim()), ParameterDirection.Input);
            AddSQLParam("@LOGIN", Consts.Funcoes.ValueOrDBNull(this._LOGIN.ToUpper().Trim()), ParameterDirection.Input);
            AddSQLParam("@PERFIL_ID", Consts.Funcoes.ZeroOrDBNull(this._PERFIL_ID), ParameterDirection.Input);
            AddSQLParam("@SENHA", DBNull.Value, ParameterDirection.Input);
            AddSQLParam("@STATUS", Consts.Funcoes.ValueOrDBNull(this._STATUS.ToUpper().Trim()), ParameterDirection.Input);
            List<TCampoCadastro> res = executeStoredProcedure("SP_INSERE_USUARIO", true);
 
            if (st = (res.Count > 0))
            {
                _USUARIO_ID = Consts.Funcoes.NullOrInt(res[0].Valor);
                TrocarSenha(_SENHA);
                fMsgInfo = "Inserido com sucesso!";
            }
            return st;
        }
 
        public bool Atualizar()
        {
            bool st = false;
            _UpdateSQL = "UPDATE USUARIOS set USUARIO_ID = @USUARIO_ID, NOME = @NOME, LOGIN = @LOGIN, PERFIL_ID = @PERFIL_ID, STATUS = @STATUS where USUARIO_ID = @USUARIO_ID ";
            ClearSQLParams();
            AddSQLParam("@USUARIO_ID", Consts.Funcoes.ZeroOrDBNull(this._USUARIO_ID), ParameterDirection.Input);
            AddSQLParam("@NOME", Consts.Funcoes.ValueOrDBNull(this._NOME.ToUpper().Trim()), ParameterDirection.Input);
            AddSQLParam("@LOGIN", Consts.Funcoes.ValueOrDBNull(this._LOGIN.ToUpper().Trim()), ParameterDirection.Input);
            AddSQLParam("@PERFIL_ID", Consts.Funcoes.ZeroOrDBNull(this._PERFIL_ID), ParameterDirection.Input);
            AddSQLParam("@STATUS", Consts.Funcoes.ValueOrDBNull(this._STATUS.ToUpper().Trim()), ParameterDirection.Input);
 
            if (st = Salvar(OperacaoBD.opUpdate, true))
            {
                fMsgInfo = "Atualizado com sucesso!";
            }
            return st;
        }
 
        public bool _Excluir()
        {
            _DeleteSQL = "delete from USUARIOS where USUARIO_ID = @USUARIO_ID";
            ClearSQLParams();
            AddSQLParam("@USUARIO_ID", Consts.Funcoes.ZeroOrDBNull(this._USUARIO_ID), ParameterDirection.Input);
 
            bool st = false;
            if (st = Salvar(OperacaoBD.opDelete, true))
            {
                fMsgInfo = "Excluído com sucesso!";
            }
            return st;
        }
 
        private List<TUsuario> ListaGenerica()
        {
            List<TUsuario> l = new List<TUsuario>();
            DataTable dt = this.getTable(_SqlSearch, (SQLParams.Count > 0));
            foreach (DataRow r in dt.Rows)
            {
                l.Add(new TUsuario());
                l[l.Count - 1].UsuarioId = Consts.Funcoes.NullOrInt(r["USUARIO_ID"]);
                l[l.Count - 1].Nome = Consts.Funcoes.NullOrString(r["NOME"]);
                l[l.Count - 1].Login = Consts.Funcoes.NullOrString(r["LOGIN"]);
                l[l.Count - 1].PerfilId = Consts.Funcoes.NullOrInt(r["PERFIL_ID"]);
                l[l.Count - 1].Status = Consts.Funcoes.NullOrString(r["STATUS"]);
            }
            return l;
        }
 
        public List<TUsuario> ListarTodos()
        {
            _SqlSearch = "select * from USUARIOS";
            
            return ListaGenerica();
        }
 
        #endregion
 
        #region Parte BO
 
        public static List<TUsuario> Listar(string pNome, string pLogin, string pStatus, int pPerfil)
        {
            List<TUsuario> results = new List<TUsuario>();
            TUsuario u = new TUsuario();
            try
            {
                u._SqlSearch = "select * from USUARIOS where (1=1) ";
                if (!pNome.Equals(""))
                {
                    u._SqlSearch += " and NOME like @NOME ";
                    u.AddSQLParam("@NOME", pNome.Trim().ToUpper() + "%", ParameterDirection.Input);
                }
                if (!pLogin.Equals(""))
                {
                    u._SqlSearch += " and LOGIN like @LOGIN ";
                    u.AddSQLParam("@LOGIN", pLogin.Trim().ToUpper() + "%", ParameterDirection.Input);
                }
                if (!pStatus.Equals(""))
                {
                    u._SqlSearch += " and STATUS = @ST ";
                    u.AddSQLParam("@ST", pStatus.Trim().ToUpper(), ParameterDirection.Input);
                }
                if (!pPerfil.Equals(0))
                {
                    u._SqlSearch += " and PERFIL_ID = @PE ";
                    u.AddSQLParam("@PE", pPerfil, ParameterDirection.Input);
                }
                results = u.ListaGenerica();
            }
            finally
            {
                u.Dispose();
            }
            return results;
        }
 
        public bool TrocarSenha(string pNovaSenha)
        {
            bool ok = false;
            //Criptografa a senha atual utilizando Hash MD5 (não poderá recuperá-la depois :-( )
            MD5CryptoServiceProvider hash = new MD5CryptoServiceProvider();
            UTF8Encoding encode = new UTF8Encoding();
            byte[] senhacripto = hash.ComputeHash(encode.GetBytes(pNovaSenha));
 
            //Agora salvamos a senha criptografada no BD.
            string isql = "update USUARIOS set SENHA = @SENHA where USUARIO_ID = @USUID";
            ClearSQLParams();
            AddSQLParam("@SENHA", senhacripto, ParameterDirection.Input);
            AddSQLParam("@USUID", this._USUARIO_ID, ParameterDirection.Input);
            if (ok = executaSQL(isql, true))
            {
                fMsgInfo = "Senha atualizada com sucesso!";
            }
            return ok;
        }
 
        private bool _Autenticar(string pLogin, string pSenha, out int pUsuID)
        {
            pUsuID = 0;
            string isql = "select USUARIO_ID,SENHA from USUARIOS where LOGIN = @LOGIN";
            ClearSQLParams();
            AddSQLParam("@LOGIN", pLogin.Trim().ToUpper(), ParameterDirection.Input);
            DataTable dt = getTable(isql, true);
            if (dt.Rows.Count > 0)
            { 
                //Criptofrafa a senha atual
                MD5CryptoServiceProvider hash = new MD5CryptoServiceProvider();
                UTF8Encoding encode = new UTF8Encoding();
                byte[] senhacripto = hash.ComputeHash(encode.GetBytes(pSenha));
 
                //Obtém a senha atual do BD
                byte[] pwd = (byte[])dt.Rows[0][1];
                if (Consts.Funcoes.CompararIgualdadeByteArray(pwd, senhacripto))
                {
                    pUsuID = (int)dt.Rows[0][0];
                    return true;
                }
                else
                {
                    fMsgInfo = "Usuário ou senha inválidos";
                    return false;
                }
            }
            else
            {
                fMsgInfo = "Usuário inexistente.";
                return false;
            }
        }
 
        public static TUsuario Autenticar(string pLogin, string pSenha, out string Mensagem)
        {
            TUsuario u = new TUsuario();
            Mensagem = "";
            int pID = 0;
            if (u._Autenticar(pLogin, pSenha, out pID))
            {
                u._USUARIO_ID = pID;
                u.SetByID();
                return u;
            }
            else
            {
                Mensagem = u.MsgInfo;
                return null;
            }
        }
 
        public static bool Excluir(int pID, out string msg)
        {
            TUsuario p = new TUsuario();
            bool st = false;
            try
            {
                p._USUARIO_ID = pID;
                st = p._Excluir();
                msg = p.MsgInfo;
            }
            finally
            {
                p.Dispose();
            }
            return st;
        }
 
 
        #endregion
    }
}

Coloquei o código completo da classe TUsuario (eita mania de Delphero colocar "T" nos nomes das classes hehe), assim é melhor para acompanhar a explicação, né?

Temos aqui campos (e as suas respectivas propriedades com get/set) para guardar o ID, Nome, Login, ID do Perfil e Status do Usuário. Também temos um campo que guarda o perfil completo (incluindo os módulos que este usuário pode acessar) do tipo TPerfilUsuario.
Também fazemos as pesquisas seguindo o modelo da classe TPerfilUsuario, ou seja, temos uma variável que guarda uma instrução SQL e um método genérico que retorna os campos ditos por esta instrução SQL, e os demais métodos de pesquisa apenas manipulam esta instrução.

Os métodos de inserção, atualização e exclusão fazem chamadas aos métodos da classe Conexao, com o método de inserção executando uma stored procedure que retorna o ID gerado e o Atualizar e o Excluir utilizando instruções SQL simples, fazendo chamada ao método Salvar, de Conexao.
O método Inserir() tem uma coisa interessante: Após salvar o registro através da stored procedure, caso tenha sucesso é chamado o método TrocarSenha.

Lembra que o campo SENHA no nosso banco de dados está definido como BLOB do tipo binário e na classe a propriedade foi definida como String? Então, neste blob guardaremos na verdade o Hash MD5 da senha informada na propriedade Senha.
Vamos destacar o método TrocarSenha:

public bool TrocarSenha(string pNovaSenha)
{
    bool ok = false;
    //Criptografa a senha atual utilizando Hash MD5 (não poderá recuperá-la depois :-( )
    MD5CryptoServiceProvider hash = new MD5CryptoServiceProvider();
    UTF8Encoding encode = new UTF8Encoding();
    byte[] senhacripto = hash.ComputeHash(encode.GetBytes(pNovaSenha));
 
    //Agora salvamos a senha criptografada no BD.
    string isql = "update USUARIOS set SENHA = @SENHA where USUARIO_ID = @USUID";
    ClearSQLParams();
    AddSQLParam("@SENHA", senhacripto, ParameterDirection.Input);
    AddSQLParam("@USUID", this._USUARIO_ID, ParameterDirection.Input);
    if (ok = executaSQL(isql, true))
    {
        fMsgInfo = "Senha atualizada com sucesso!";
    }
    return ok;
}

Primeiramente, inicializamos a classe MD5CryptoServiceProvider, que será a responsável por calcular o MD5 da string que passaremos como senha. O método ComputeHash desta função retorna um array de bytes com o hash calculado e recebe como parâmetro um outro array de bytes com a informação a qual iremos calcular este hash. Instanciamos um objeto da classe UTF8Encoding justamente para transformar esta string em um array de bytes compatível com o método ComputeHash.
Em seguida, damos um UPDATE via instrução SQL no registro correspondente ao usuário que irá ter a senha trocada. Se tudo der certo, retornamos true.

Agora, vamos destacar os métodos que realizam a autenticação do usuário no sistema:

private bool _Autenticar(string pLogin, string pSenha, out int pUsuID)
{
    pUsuID = 0;
    string isql = "select USUARIO_ID,SENHA from USUARIOS where LOGIN = @LOGIN";
    ClearSQLParams();
    AddSQLParam("@LOGIN", pLogin.Trim().ToUpper(), ParameterDirection.Input);
    DataTable dt = getTable(isql, true);
    if (dt.Rows.Count > 0)
    { 
        //Criptofrafa a senha atual
        MD5CryptoServiceProvider hash = new MD5CryptoServiceProvider();
        UTF8Encoding encode = new UTF8Encoding();
        byte[] senhacripto = hash.ComputeHash(encode.GetBytes(pSenha));
 
        //Obtém a senha atual do BD
        byte[] pwd = (byte[])dt.Rows[0][1];
        if (Consts.Funcoes.CompararIgualdadeByteArray(pwd, senhacripto))
        {
            pUsuID = (int)dt.Rows[0][0];
            return true;
        }
        else
        {
            fMsgInfo = "Usuário ou senha inválidos";
            return false;
        }
    }
    else
    {
        fMsgInfo = "Usuário inexistente.";
        return false;
    }
}
 
public static TUsuario Autenticar(string pLogin, string pSenha, out string Mensagem)
{
    TUsuario u = new TUsuario();
    Mensagem = "";
    int pID = 0;
    if (u._Autenticar(pLogin, pSenha, out pID))
    {
        u._USUARIO_ID = pID;
        u.SetByID();
        return u;
    }
    else
    {
        Mensagem = u.MsgInfo;
        return null;
    }
}

Temos o método privado _Autenticar que fará a validação do usuário/senha informados no momento do login. A verificação do login é feita através de uma instrução SELECT, a qual retornaremos seu resultado num DataTable.
Se o login informado existir na base de dados, aí sim é feita a verificação da senha. Como a função MD5 é de mão única, ou seja, não podemos obter a senha em formato de string a partir de seu hash, para poder comparar a senha informada com a armazenada no BD precisamos calcular o hash MD5 da senha informada no login e por fim comparar os dois hashs.
Só que agora tem um probleminha: Se fizermos a comparação direta de dois arrays utilizando o operador "==" (para variáveis do tipo array não existe o método Equals), esta comparação irá retornar sempre falsa, ou seja, ela nem é feita por sinal. Uma forma de contornar isso é comparar o array elemento a elemento. Para fazer tal comparação, criei um método chamado CompararIgualdadeByteArray, que faz a comparação entre dois arrays de bytes. Caso todos os elementos sejam iguais, retornará true.
Segue o método:

public static bool CompararIgualdadeByteArray(byte[] a1, byte[] a2)
{
    bool r = true;
    for (int i = 0; i < a1.Length; i++)
    {
        if (a2[i] != a1[i])
        {
            r = false;
            break;
        }
    }
    return r;
}

Esta é uma função simples, que varre cada elemento do primeiro array e verifica se na mesma posição do segundo array há um elemento igual. Se houver alguma diferença, a função é abortada e retorna falso. No nosso exemplo, ela está dentro do arquivo Consts.Funcoes.
Como eu tenho certeza que pelo menos neste projeto estarei trabalhando com arrays de mesmo tamanho (16 bytes) e mesmo tipo de dado em cada elemento, não fiz a verificação do tamanho dos arrays :-)

Voltando à autenticação... Após a senha ser verificada, caso esteja OK a função irá retornar true e o ID do usuário correspondente no argumento de saída pUsuID.

Note que o método _Autenticar é declarado como privado. Iremos expor este método através do método público e estático Autenticar, em que iremos informar o usuário e a senha e se a autenticação for bem sucedida irá retornar uma instância de TUsuario, com todas as informações do usuário (inclusive perfil e módulos que pode acessar) carregadas.

É isso! Agora já temos as classes necessárias, no próximo artigo iremos implementar a interface em ASP.NET que irá manipular este cadastro.

Exemplo Sistema de Login em ASP.NET (com BD Firebird)  (289 kB)

Até lá e abraço a todos!

[Update 26/02/2008: Para facilitar o download, ao invés da página de suporte hospedada no Geocities, estarei movendo os arquivos para hospedagem própria, diretamente no domínio leonelfraga.com e colocando os links diretos para o arquivo.]

Leonel Fraga de Oliveira Leonel Fraga de Oliveira é formado em Processamento de Dados na Faculdade de Tecnologia de São Paulo (FATEC-SP - 2002) e anteriormente em Técnico em Eletrônica, pela ETE Professor Aprígio Gonzaga (lá em 1999).
Atualmente trabalha como Analista de Sistemas na Prefeitura Municipal de São Caetano do Sul - SP
Tem como hobbies DJing (também trabalha como DJ freelancer) e ciclismo, além da manutenção dos sites NeoMatrix Light e NeoMatrix Tech.
Gosta de música eletrônica, tecnologia, cinema (super fã de Jornada nas Estrelas), gastronomia e outras coisas mais.


Compartilhe nas redes sociais

   

Deixe seu comentário

comments powered by Disqus