Controle de Usuários para sistemas ASP.NET - Parte 1: Modelando o BD
Leonel Fraga de Oliveira 14/08/2008 23:14

Olá pessoal! Agora inicio uma série de artigos em que iremos construir um módulo de controle de acesso para sistemas feitos em ASP.NET, sob medida, sem nos prendermos aos componentes e/ou métodos de autenticação já internos (Windows Authentication, Forms Authentication, Passport, etc).

O controle de acesso consistirá de uma biblioteca de classes, utilizando o framework de conexão já explanado aqui no Blog. Utilizaremos como "cobaia" o nosso exemplo de gridview do artigo anterior, ou seja, vamos colocar nele o controle de acesso :-)

Ele já está totalmente pronto para baixar na nossa página de suporte, caso você queira ir adiantando as coisas pro seu lado ;-)

Iremos colocar uma rotina em cada página (tá, pode ser em uma só, mas depois explico como!) de nossos sistemas que verifica se determinado perfil de usuário tem acesso àquela página. Portanto, pela idéia básica acima, trabalharemos em cima de perfis de usuário.

Vamos primeiramente modelar a base de dados (somente os objetos relacionados ao controle de acesso) que será a base do nosso projeto. Observem o diagrama abaixo:

Diagrama do Banco de Dados do Controle de Usuário

Iremos utilizar nos exemplos o banco de dados Firebird 2.0.

Temos uma tabela onde iremos guardar os perfis de usuários (USR_PERFIL), com um código e uma descrição, outra tabela onde iremos guardar os nomes das páginas que fazem parte do nosso sistema (SYS_MODULOS), cada página recebe um código, uma descrição, e no campo PAGINA_WEB será o nome do formulário sem a extensão .aspx. Temos também uma tabela onde relacionaremos cada perfil com os módulos que ele poderá acessar (PERFIL_MODULOS).

Por fim, temos a nossa tabela de Usuários (USUARIOS). Note que o campo SENHA foi definido como um blob binário, e não como VARCHAR na como geralmente é feito. Iremos guardar aí o Hash MD5 da senha em sua forma binária, ou seja, antes de gravarmos a senha no banco calcularemos seu Hash MD5 através do namespace System.Security.Cryptography do .NET e guardaremo-os no BD na sua forma binária (ou na visão de nossa biblioteca de classes, um array de bytes). Portanto, quando fizermos a rotina de autenticação, primeiro calcularemos o Hash da senha informada no momento do login e iremos comparar com o hash armazenado no banco. Esta tabela também possui um campo de STATUS, que poderá ser "A"tivo ou "I"nativo. Esta tabela relaciona-se com a tabela PERFIL, que por sua vez definirá quais módulos este usuário pode acessar.

Criaremos também em nossa base de dados alguns generators, uma exception e algumas stored-procedures, conforme segue:

/*Generators*/
create generator GEN_PERFIL_ID;
create generator GEN_USUARIO_ID;

/*Exceptions*/
CREATE EXCEPTION EXC_EXISTE_USUARIO 'Login já existente no sistema. Indique outro :-)';

/*Stored Procedures*/
--Inserção do Perfil
CREATE PROCEDURE SP_INSERE_PERFIL (
    descricao varchar(100))
returns (
    perfil_id integer)
as
begin
    PERFIL_ID = gen_id(gen_perfil_id,1);
    insert into USR_PERFIL (PERFIL_ID,DESCRICAO) values (:perfil_id,:descricao);
    suspend;
end

--Inserção do Usuário
CREATE PROCEDURE SP_INSERE_USUARIO (
    nome varchar(100),
    login varchar(15),
    perfil_id integer,
    senha blob sub_type 0 segment size 80,
    status char(1))
returns (
    usuario_id integer)
as
begin
    if (not exists(select * from USUARIOS where LOGIN = :login)) then
    begin
        usuario_id = gen_id(gen_usuario_id,1);
        insert into USUARIOS (USUARIO_ID,NOME,LOGIN,PERFIL_ID,SENHA,STATUS) values (:usuario_id,:nome,:login,:perfil_id,:senha,:status);
    end
    else begin
        exception exc_existe_usuario;
    end
    suspend;
end

--Salvamento de Item do Controle de Acesso
CREATE PROCEDURE SP_SALVA_PERFIL_MODULO (
    perfil_id integer,
    modulo_id integer,
    tem_acesso integer)
as
begin
    if (TEM_ACESSO = 1) then begin
        if (not exists(select * from PERFIL_MODULOS where MODULO_ID = :modulo_id and PERFIL_ID = :perfil_id)) then
            insert into PERFIL_MODULOS (MODULO_ID,PERFIL_ID) values (:modulo_id,:perfil_id);
    end
    if (TEM_ACESSO = 0) then begin
        if (exists(select * from PERFIL_MODULOS where MODULO_ID = :modulo_id and PERFIL_ID = :perfil_id)) then
            delete from PERFIL_MODULOS where MODULO_ID = :modulo_id and PERFIL_ID = :perfil_id;
    end
end

Os dois generators servirão para gerar os ID's do usuário e perfil automaticamente.

A inserção do perfil será feita através da procedure SP_INSERE_PERFIL, onde é passada a descrição como parâmetro e é retornado o ID do perfil salvo. A atualização da descrição do perfil será feita através de uma instrução UPDATE na biblioteca de classes.

Relacionado também ao perfil do usuário, temos a procedure SP_SALVA_PERFIL_MODULO, que recebe como parâmetros os ID's do Perfil e do Módulo e um flag indicando se possui acesso (1) ou não (0).

Se para uma determinada combinação de módulo e perfil for passado o flag "1", a SP checará se já existe esta combinação na tabela PERFIL_MODULOS. Caso possua, não é feito nada, e caso ainda não possua, ou seja, o perfil não tem acesso ao módulo (sim, o acesso ao módulo pelo perfil se dará na existência da combinação perfil x módulo na tabela PERFIL_MODULOS) um registro é inserido.

Caso o flag TEM_ACESSO seja igual a "0" e a combinação exista na tabela PERFIL_MODULOS, a mesma é excluída, ou seja, estamos retirando a permissão de acesso ao módulo para aquele perfil).

Temos também a procedure SP_INSERE_USUARIO, que primeiro checa se o login informado já existe. Se existir, é disparada a exceção EXC_EXISTE_USUARIO, e a mensagem da mesma é informada para a nossa aplicação. Caso contrário, o usuário é inserido no BD e a procedure retorna o ID gerado.

Outra forma de implementar isso é colocando o campo LOGIN como chave primária :-)

Como na tabela PERFIL, a atualização dos registros será feita na forma de instruções UPDATE na biblioteca de classes.

As inserções foram feitas na forma de procedures para facilitar a implementação do controle de acesso em outros bancos de dados, e este modelo é o que atende melhor essa finalidade, na minha opinião. Por exemplo, no SQL Server podemos definir os campos de ID como identity e como retorno das procedures retornaremos o valor do identity da linha inserida, assim como no Oracle podemos criar um sequence, e retorná-lo depois, sendo que o código da aplicação não se alterará com a mudança de BD (exceto pela constante de definição de queries parametrizadas - @ para o Firebird e SQL Server e ":" para o Oracle, mas isso poderemos deixar isso automático mais para frente!).

Para começar pra valer a nossa brincadeira, devemos pré-inserir os itens da tabela SYS_MODULOS com todas as páginas do sistema em que o controle de acesso será implantado, um perfil de Administrador Geral, por exemplo, e suas associá-los na tabela PERFIL_MODULOS. No site exemplo, inseri as páginas do próprio controle de acesso e a página do exemplo de gridview turbinado que iremos controlar também.

Pela sua natureza, a tabela SYS_MODULOS não sofrerá atualização via sistema, ou seja, o usuário final não terá controle sobre ela.

O banco já está aí, no próximo artigo definiremos a classe de Perfil de Usuário.

Até lá!

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

[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