Esse artigo faz parte da revista Java Magazine edição 22. Clique aqui para ler todos os artigos desta edição

Atenção: por essa edição ser muito antiga não há arquivo PDF para download.Os artigos dessa edição estão disponíveis somente através do formato HTML. 

Java Live

Segurança no J2EE

Parte 1: Desenvolvimento e deployment no Tomcat

Aprenda a utilizar as facilidades de autenticação e controle de acesso da plataforma J2EE para aplicações web com o mais popular container web

Fernando Lozano

Quantas vezes você já construiu um módulo de segurança para um sistema ou site web? Muda a linguagem de programação, muda a empresa, muda o projeto, mas há sempre a mesma necessidade: validar senhas de usuários e permissões de acesso.

No universo J2EE não é preciso reinventar a roda. A especificação determina como uma aplicação pode definir regras de controle de acesso e como obter informações sobre o usuário logado.

Esta série, em duas partes bastante independentes, demonstra como configurar e utilizar os recursos de segurança previstos pelo J2EE para implementar políticas de autenticação e de controle de acesso, incluindo exemplos práticos de implementação nos servidores livres mais populares. O Tomcat é o foco desta primeira parte e o JBoss, o da parte final. A abordagem será prática, pela construção de um exemplo em que cada usuário pode acessar um conjunto diferente de páginas. O exemplo será depois modificado para ilustrar outros cenários freqüentemente requisitados pelos usuários.

Segurança declarativa

O J2EE utiliza uma abordagem declarativa para as configurações de segurança, baseada no conceito de roles (papéis). Uma aplicação define um ou mais roles e depois quais operações podem ser realizadas por cada um desses roles. Grosso modo, um role é um grupo de usuários. O conceito de operação depende do contexto: em um container web, consiste no acesso a uma URL; em um container EJB uma operação representa a invocação de um método remoto.

O código da aplicação não chama nenhum método para validar uma senha, nem para autorizar ou não a realização de uma operação. Fazer isso seria uma abordagem procedural. Pode-se fazer uma analogia com comandos select do SQL e comandos seek do dBase e Clipper. No SQL, o select descreve quais dados são desejados, mas não quais índices acessar e arquivos de dados abrir; no dBase/Clipper arquivos e índices são abertos manualmente, e acessos a várias tabelas se tornam loops aninhados. Comandos select são portanto uma abordagem declarativa; comandos seek, uma abordagem procedural.

É claro que nem todas as situações podem ser tratadas de forma declarativa. Por isso as APIs de Servlets e de EJB definem métodos para descobrir quem é o usuário corrente (ou se não foi ainda feito o login) e se este usuário possui ou não um role específico. Com esses métodos, é possível tratar no código da aplicação qualquer situação que não possa ser tratada pelos descritores. Vários exemplos serão apresentados ao longo do artigo.

Roles e operações são parte da estrutura da aplicação, por isso são definidas pelo desenvolvedor e especificadas no descritor (com URIs mapeadas para servlets) ou na interface dos EJBs. Por outro lado, a especificação J2EE não define como as senhas são validadas, nem como um usuário é associado a um role. Essas questões são responsabilidade do administrador do servidor de aplicações, e cada produto tem total liberdade de implementá-las da forma que for julgado mais conveniente.

Assim sendo, qualquer aplicação que utilize os recursos padrão do J2EE para autenticação e controle de acesso irá exigir um deployment (instalação) customizado para o servidor de aplicações adotado. Alguns desenvolvedores vêem isto como desvantagem, preferindo implementar seus próprios esquemas de autenticação e controle de acesso visando garantir que suas aplicações possam ser instaladas de forma fácil e inalterada em qualquer servidor do mercado. O problema é que acabam “reinventando a roda” e na maioria das vezes ficando com a segurança deficiente, por falta de conhecimento especializado no assunto.

 

Controle de acesso em aplicações web

A Listagem 1 demonstra a sintaxe para a declaração de roles e regras de controle de acesso para uma aplicação web de exemplo, que está disponível para download no site da Java Magazine. Note que são definidos dois roles – “usuario” e “administrador” – e que a página inicial da aplicação tem acesso público, pois sua URL (/index.jsp) não se enquadra nos padrões url-pattern definidos para nenhum dos elementos web-resource-collection presentes no descritor.

 

Se não for definida uma regra de controle de acesso para uma operação, esta operação poderá ser realizada livremente por usuários não-autenticados. Caso se deseje que todas as páginas de uma aplicação web estejam sob controle de acesso, deve-se inserir todas elas em um mesmo diretório, e definir um padrão como “nome_da_pasta/*”, correspondendo a todas as páginas da aplicação. Não se deve definir simplesmente “/*”, padrão que corresponde a todas as páginas da aplicação. Isso porque caso sua aplicação utilize uma página de login customizada, esta página deve estar acessível para usuários anônimos, assim como a página de erro associada.

 

Observe que, da forma como as regras de controle de acesso foram definidas, os conjuntos de páginas acessíveis para usuários e administradores são completamente separados. Caso se deseje que o administrador também possa acessar as páginas dos usuários, há três alternativas:

·        Acrescentar mais um role-name com o valor “administrador” dentro do auth-constraint do elemento web-resource-collection “Páginas do Usuário”

·        Acrescentar mais um url-pattern com o valor “/usuarios/*” dentro do auth-constraint do web-resource-collection “Páginas do Administrador”

·        Dar aos usuários que recebem o role “administrador” também a role “usuario”

 

Agora veja na Figura 1 a estrutura do pacote war da aplicação de exemplo. O conteúdo das páginas é livre, pois estamos interessados apenas em verificar quais usuários conseguem acessar quais páginas. A página index.jsp contém links para as páginas de índice em cada subdiretório, funcionando como um menu de acesso à aplicação.

Por simplicidade o exemplo consiste apenas de páginas JSP, mas as mesmas regras se aplicam a servlets, pois cada servlet é mapeado para uma ou mais URIs no descritor web.xml. A restrição de acesso baseada em URIs também se aplica a páginas estáticas, criando uma estrutura simples e uniforme para controlar o acesso a todas as páginas da aplicação.

É por isso que alguns servidores de aplicações, como o Tomcat, desabilitam em suas configurações padrão o acesso a servlets pelo nome da classe (com a URI /servlets/pacote.nomeDaClasse), apesar desse tipo de acesso ser previsto pela especificação de Servlets. O acesso pelo nome de classe permitiria que um servlet sujeito a controle de acesso pela sua URI (mapeada explicitamente no web.xml) fosse acessado por usuários não-autorizados, pois seria utilizada uma URI diferente.

 

Listagem 1. Descritor web.xml para a aplicação de exemplo

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

 

  <!-- ... mapeamentos de servlets e parâmetros de inicialização -->

 

  <security-constraint>

    <web-resource-collection>

      <web-resource-name>Paginas do Usuario

      </web-resource-name>

      <url-pattern>/usuarios/*</url-pattern>

    </web-resource-collection>

    <auth-constraint>

      <role-name>usuario</role-name>

    </auth-constraint>

  </security-constraint>

 

  <security-constraint>

    <web-resource-collection>

      <web-resource-name>Paginas do Administrador</web-resource-name>

      <url-pattern>/admin/*</url-pattern>

    </web-resource-collection>

    <auth-constraint>

      <role-name>administrador</role-name>

    </auth-constraint>

  </security-constraint>

 

  <security-role>

    <role-name>usuario</role-name>

  </security-role>

  <security-role>

...

Quer ler esse conteúdo completo? Tenha acesso completo