Controle de Usuários para sistemas ASP.NET - Parte 1: Modelando o BD
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:
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:
1: /*Generators*/
2: create generator GEN_PERFIL_ID;
3: create generator GEN_USUARIO_ID;
4:
5: /*Exceptions*/
6: CREATE EXCEPTION EXC_EXISTE_USUARIO 'Login já existente no sistema. Indique outro :-)';
7:
8: /*Stored Procedures*/
9: --Inserção do Perfil
10: CREATE PROCEDURE SP_INSERE_PERFIL (
11: descricao varchar(100))
12: returns (
13: perfil_id integer)
14: as
15: begin
16: PERFIL_ID = gen_id(gen_perfil_id,1);
17: insert into USR_PERFIL (PERFIL_ID,DESCRICAO) values (:perfil_id,:descricao);
18: suspend;
19: end
20:
21: --Inserção do Usuário
22: CREATE PROCEDURE SP_INSERE_USUARIO (
23: nome varchar(100),
24: login varchar(15),
25: perfil_id integer,
26: senha blob sub_type 0 segment size 80,
27: status char(1))
28: returns (
29: usuario_id integer)
30: as
31: begin
32: if (not exists(select * from USUARIOS where LOGIN = :login)) then
33: begin
34: usuario_id = gen_id(gen_usuario_id,1);
35: insert into USUARIOS (USUARIO_ID,NOME,LOGIN,PERFIL_ID,SENHA,STATUS) values (:usuario_id,:nome,:login,:perfil_id,:senha,:status);
36: end
37: else begin
38: exception exc_existe_usuario;
39: end
40: suspend;
41: end
42:
43: --Salvamento de Item do Controle de Acesso
44: CREATE PROCEDURE SP_SALVA_PERFIL_MODULO (
45: perfil_id integer,
46: modulo_id integer,
47: tem_acesso integer)
48: as
49: begin
50: if (TEM_ACESSO = 1) then begin
51: if (not exists(select * from PERFIL_MODULOS where MODULO_ID = :modulo_id and PERFIL_ID = :perfil_id)) then
52: insert into PERFIL_MODULOS (MODULO_ID,PERFIL_ID) values (:modulo_id,:perfil_id);
53: end
54:
55: if (TEM_ACESSO = 0) then begin
56: if (exists(select * from PERFIL_MODULOS where MODULO_ID = :modulo_id and PERFIL_ID = :perfil_id)) then
57: delete from PERFIL_MODULOS where MODULO_ID = :modulo_id and PERFIL_ID = :perfil_id;
58: end
59: 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.]
0 comentários:
Postar um comentário