Autenticação de Usuários em uma Aplicação JSF (no JBoss)

Já faz algum tempo que estou devendo este tutorial a alguns dos meus ex-alunos do curso de pós-graduação da TNT de Campo Grande. Então, antes de tudo, ele representa o pagamento de uma dívida =). Peço desculpas pela demora, e espero que valha a pena a espera.

Vou mostrar com este tutorial como publicar uma aplicação JavaServer Faces no JBoss, e configurar a autenticação programática de usuários, baseada em banco de dados.

Apesar de ser apresentada a configuração básica de uma aplicação JSF baseada no Richfaces, o foco do tutorial não é explicar a fundo como usar este framework.

A intenção principal com o tutorial é mostrar como configurar a autenticação de usuários baseada em banco de dados, e utilizar recursos do Richfaces que tiram proveito da autenticação programática do container para ocultar determinados componentes. Ao final deixarei dicas de referências para saber mais sobre as tecnologias.

Vou utilizar como ambiente de desenvolvimento o Eclipse Galileo com o JBoss Tools. Há algum tempo eu publiquei um tutorial mostrando como configurar este ambiente.

A versão do JBoss que utilizarei é a 5.1.0. Há pouco publiquei um how-to mostrando como configurá-lo no Eclipse.

O termo JBOSS_HOME usado ao longo do tutorial refere-se ao diretório de instalação do JBoss.

Mão na Massa!

Considerando que seu Eclipse já está configurado de acordo com o tutorial citado acima, e que o JBoss já foi adicionado, vamos criar uma aplicação JavaServer Faces através da opção oferecida pelo JBoss Tools.

Configure a aplicação como nas próximas imagens:

Faça o download de uma versão estável do RichFaces no link http://www.jboss.org/richfaces/download/stable.html.

Para este tutorial utilizei a versão 3.3.3 (3.3.3.Final Binary (zip)).

Copie os jars do RichFaces para o diretório WEB-INF/lib do projeto que acabamos de criar, como mostra a imagem abaixo:

Agora vamos configurar o projeto para que dê suporte ao RichFaces. Edite o arquivo web.xml do projeto para que fique como apresentado abaixo:


<?xml version="1.0"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <display-name>WebAut</display-name>

 <context-param>
 <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
 <param-value>server</param-value>
 </context-param>

 <context-param>
 <param-name>org.richfaces.SKIN</param-name>
 <param-value>blueSky</param-value>
 </context-param>

 <context-param>
 <param-name>org.richfaces.CONTROL_SKINNING</param-name>
 <param-value>enable</param-value>
 </context-param>

 <filter>
 <display-name>RichFaces Filter</display-name>
 <filter-name>richfaces</filter-name>
 <filter-class>org.ajax4jsf.Filter</filter-class>
 </filter>

 <filter-mapping>
 <filter-name>richfaces</filter-name>
 <servlet-name>Faces Servlet</servlet-name>
 <dispatcher>REQUEST</dispatcher>
 <dispatcher>FORWARD</dispatcher>
 <dispatcher>INCLUDE</dispatcher>
 </filter-mapping>

 <listener>
 <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
 </listener>

 <!-- Faces Servlet -->
 <servlet>
 <servlet-name>Faces Servlet</servlet-name>
 <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
 <load-on-startup>1</load-on-startup>
 </servlet>

 <!-- Faces Servlet Mapping -->
 <servlet-mapping>
 <servlet-name>Faces Servlet</servlet-name>
 <url-pattern>*.jsf</url-pattern>
 </servlet-mapping>

 <welcome-file-list>
 <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

Agora crie uma página chamada index.jsp na raiz do diretório web, como na imagem abaixo:

Edite a página como apresentado abaixo:


<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head></head>
<body>
<jsp:forward page="/paginas/inicial.jsf" />
</body>
</html>

Agora crie um diretório chamado “paginas” na raiz do diretório web, e dentro dele um arquivo chamado inicial.jsp, com o conteúdo abaixo:

<%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
 <f:view>
 <head>
 <title></title>
 </head>
 <body>
 <div style="width: 300px;">
 <h:form id="formExemplo">
 <rich:panel header="Painel Administrativo">
 Coisas que somente administradores podem ver!
 </rich:panel>
 <rich:spacer height="20"/>
 <rich:panel header="Painel Comum">
 Coisas que qualquer usuário pode ver!
 </rich:panel>
 </h:form>
 </div>
 </body>
 </f:view>
</html>

O projeto deve estar como na imagem abaixo:

Agora vamos testar o que fizemos até agora.

Exporte o projeto como um arquivo war, e salve-o no diretório deploy do servidor, conforme a imagem abaixo:

Inicialize o servidor através da opção de menu disponível no painel Servers:

Após iniciar o servidor, acesse o endereço da aplicação no navegador:

http://localhost:8080/WebAut/

A aplicação deverá ser apresentada, como na imagem a seguir:

Ok! Até agora o que fizemos foi criar uma aplicação simples de exemplo. Vamos agora criar uma base de dados para armazenar os dados do usuário. Para isso vou utilizar o MySQL 5.0.

Crie uma base de dados chamada “credencial” com a estrutura de tabelas como a apresentada abaixo:

create table users (
  user_name         varchar(15) not null primary key,
  user_pass         varchar(15) not null
);

create table user_roles (
  user_name         varchar(15) not null,
  role_name         varchar(15) not null,
  primary key (user_name, role_name)
);

Insira os seguintes dados de exemplo:


insert into users (user_name, user_pass) values ('chuck', 'norris');
insert into users (user_name, user_pass) values ('bond', 'james');

insert into user_roles (user_name, role_name) values ('chuck', 'MASTER');
insert into user_roles (user_name, role_name) values ('bond', 'COMMON');

Agora vamos criar os arquivos de configuração necessários no JBoss. Para isso, pare o servidor.

O primeiro dos arquivos é o de configuração de datasource.

Crie um arquivo chamado webaut-ds.xml dentro do diretório deploy do JBoss (JBOSS_HOME\server\default\deploy). É importante que ele tenha o sufixo -ds, como sugerido. O arquivo deve conter o seguinte conteúdo (alterando-se, é claro, os dados de conexão):


<datasources>
 <local-tx-datasource>
 <jndi-name>WebAutDS</jndi-name>
 <connection-url>jdbc:mysql://localhost:3306/credencial</connection-url>
 <driver-class>com.mysql.jdbc.Driver</driver-class>
 <user-name>root</user-name>
 <password>mysql</password>
 <metadata>
 <type-mapping>mySQL</type-mapping>
 </metadata>
 </local-tx-datasource>
</datasources>

Esta configuração é o datasource da aplicação. Ela poderia ser usada pela nossa aplicação para obter conexões com o banco de dados, mas no caso do nosso exemplo, ela será usada pelo módulo que fará a autenticação dos usuários.

A seguir, procure o arquivo login-config.xml,  dentro do diretório conf (JBOSS_HOME\server\default\deploy\conf), e adicione a ele o seguinte conteúdo:


<application-policy name = "WebAutRealm">
 <authentication>
 <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule"
 flag = "required">
 <module-option name = "dsJndiName">java:/WebAutDS</module-option>
 <module-option name = "principalsQuery">select user_pass from users where user_name=?</module-option>
 <module-option name = "rolesQuery">select role_name, 'Roles' from user_roles where user_name=?</module-option>
 </login-module>
 </authentication>
 </application-policy>

Esta configuração é a responsável por autenticar os usuários da aplicação. Ela utiliza o datasource que foi criado anteriormente para conseguir uma conexão com o banco, e realiza as consultas das credenciais dos usuários de acordo com as sql’s informadas.

Esta configuração será referenciada na aplicação, através de seu nome (WebAutRealm), nas configurações que faremos a seguir.

Antes de fazermos as alterações na aplicação, copie um driver jdbc para o MySQL no diretório lib (JBOSS_HOME\server\default\deploy\lib).

Vamos agora ajustar a aplicação!

Crie um arquivo chamado jboss-web.xml dentro do diretório WEB-INF, e configure seu conteúdo como mostrado a seguir:


<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/WebAutRealm</security-domain>
</jboss-web>

Repare que estamos usando o nome JNDI da configuração de login que fizemos há pouco.

Agora, adicione os seguintes conteúdos no arquivo web.xml (conteúdo é intercalado com a explicação do que cada trecho significa).

  • Aqui identifico quais recursos estão protegidos, e quais perfis têm acesso aos recursos. Neste caso estou informando que todo o conteúdo (/*) está seguro, e que somente os perfis MASTER e COMMON terão acesso:

<security-constraint>
 <web-resource-collection>
 <web-resource-name>Area Restrita</web-resource-name>
 <url-pattern>/*</url-pattern>
 <http-method>POST</http-method>
 <http-method>GET</http-method>
 </web-resource-collection>
 <auth-constraint>
 <role-name>MASTER</role-name>
 <role-name>COMMON</role-name>
 </auth-constraint>
 </security-constraint>
  • Aqui estou informando que o método de login será através de um formulário web:
 <!-- METODO DE LOGIN PARA VALIDACAO DE ACESSO -->
 <login-config>
 <auth-method>FORM</auth-method>
 <form-login-config>
 <form-login-page>/login.jsp</form-login-page>
 <form-error-page>/login.jsp</form-error-page>
 </form-login-config>
 </login-config>

  • Aqui estou informando quais os possíveis perfis de acesso:
<!-- ROLES -->
 <security-role>
 <role-name>MASTER</role-name>
 </security-role>
 <security-role>
 <role-name>COMMON</role-name>
 </security-role>

Agora crie um arquivo chamado login.jsp na base do diretório web, com o seguinte conteúdo:

<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 </head>
 <body>

 <form method="post" action="j_security_check">
 <fieldset>
 <legend>Login</legend>
 <p>
 <label for="form-login">Login:</label>
 <input type="text" name="j_username" id="form-login" />
 </p>
 <p>
 <label for="form-senha">Senha:</label>
 <input type="password" name="j_password" id="form-senha" />
 </p>
 <p>
 <input type="submit" value="Enviar" />
 </p>
 </fieldset>
 </form>
 </body>
</html>

Por fim, faça a seguinte alteração na página inicial.jsp:

...
<h:form id="formExemplo">
 <rich:panel header="Painel Administrativo"
 rendered="#{rich:isUserInRole('MASTER')}">
 Coisas que somente administradores podem ver!
 </rich:panel>
 <rich:spacer height="20"/>
 <rich:panel header="Painel Comum"
 rendered="#{rich:isUserInRole('MASTER, COMMON')}">
 Coisas que qualquer usuário pode ver!
 </rich:panel>
 </h:form>
...

A propriedade rendered na tag indica se a mesma deve ou não ser apresentada. A instrução rich:isUserInRole verifica se o usuário logado possui a autorização informada.
Em resumo, o componente somente será mostrado se o usuário possuir a permissão informada.

No momento a estrutura de arquivos de seu projeto deve estar como a da imagem a seguir:

Gere novamente um war da aplicação, e publique-a no diretório deploy (JBOSS_HOME\server\default\deploy). Apague os diretórios work e temp que estão no diretório default (JBOSS_HOME\server\default).

Inicie o servidor, e assim que ele estiver no ar acesse a aplicação. Uma tela de login deverá aparecer:

Para o Chuck Norris, como era de se esperar, os dois painéis aparecem:

Agora reinicie o servidor, e faça o login como James Bond:

Como era de se esperar, somente o painel comum é apresentado:

Obviamente que há muitas maneira de melhorar o exemplo, como colocar uma opção para logoff na página, adicionar mais propriedades nos arquivos de configuração, mas isso vai ficar como um exercício adicional para o leitor 😉

A seguir listo algumas referências para pesquisa, e ao final coloco um link para o download dos arquivos usados no post.

Referências:

http://java.sun.com/javaee/javaserverfaces/

http://www.jboss.org/richfaces

http://livedemo.exadel.com/richfaces-demo/index.jsp

http://community.jboss.org/wiki/DatabaseServerLoginModule

http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.2/doc/Server_Configuration_Guide/Using_JBoss_Login_Modules-DatabaseServerLoginModule.html

http://community.jboss.org/wiki/configdatasources

http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.2/doc/Server_Configuration_Guide/Connectors_on_JBoss-Configuring_JDBC_DataSources.html

http://docs.sun.com/app/docs/doc/819-3201/6n5eht3o4?a=view

http://www.cafesoft.com/products/cams/tomcat-security.html

Download dos arquivos do tutorial

Anúncios

Configurando o Tomcat 6.x para acessar uma aplicação EJB3 no GlassFish 2.x

Esta é uma dica rápida, e não um tutorial completo, de como configurar os servidores Tomcat e GlassFish para que uma aplicação web/JSF publicada no Tomcat possa acessar um EJB remoto em um servidor GlassFish.

Não irei apresentar detalhes de como implementar as aplicações (páginas, Managed Beans, EJB, etc…), apenas dar uma dica de como configurar o ambiente para esta interação (bibliotecas, arquivos de configuração, etc…), uma vez que a configuração inicial pode ser “chata” o suficiente para tirar a paciência de qualquer cristão.

As versões usadas nos testes foram as seguintes:

Tomcat 6.0.20

GlassFish 2.1

Imagine o cenário: você quer escrever uma aplicação web, com JavaServer Faces (Richfaces) e publicar esta aplicação no Tomcat. Você quer desenvolver seus EJBs 3.0 e publicá-los no GlassFish, e fazer com que sua aplicação que está no Tomcat possa acessá-los.

O cenário parece simples, acessar remotamente um EJB. O problema são as benditas configurações a serem feitas, e principalmente, a falta de informações detalhadas para este cenário especificamente: Tomcat + GlassFish. Procurando referências de configurações de componentes EJB3.0 no GlassFish chegamos à GlassFish EJB FAQ. Nela encontramos a informação inicial de que, para uma aplicação acessar remotamente um EJB 3.0 no GlassFish precisamos de alguns jars no classpath do projeto:

For GlassFish v2 and earlier

Include both $GLASSFISH_HOME/lib/appserv-rt.jar and $APS_HOME/lib/javaee.jar in the client’s classpath.

E.g., assuming the application classes are in /home/user1/myclasses and the main client class is acme.MyClient :

java -classpath $GLASSFISH_HOME/lib/appserv-rt.jar:$GLASSFISH_HOME/lib/javaee.jar:/home/user1/myclasses acme.MyClient

Note : appserv-rt.jar uses the JAR MANIFEST-CLASSPATH attribute to include other dependent .jars within the lib directory.  When setting your client classpath, it is best to directly refer to the appserv-rt.jar within the app server lib directory rather  than copying it to another location.  If you do copy appserv-rt.jar, you’ll need to copy additional .jars such as  appserv-deployment-client.jar and appserv-ext.jar.  The full set of .jars that might be needed by appserv-rt.jar is located in its META-INF/MANIFEST.MF file.

Para resimir isto aí tudo, para as versões dos servidores que eu citei no início, os jars necessários são os seguintes, encontrados no diretório lib da instalação do GlassFish:

Ok! Se você jogar estes jars diretamente no diretório lib do Tomcat, inúmeras exceções serão apresentadas ao executar a aplicação. Seguindo a dica deste post, deve-se criar um diretório shared/lib na raiz do diretório de instalação do Tomcat, para que estas exceções não ocorram.

O grande problema desta dica é que, segundo o que parece, os jars são reconhecidos automaticamente, mas a realidade é bem diferente. Depois de muito quebrar a cabeça descobri que é necessário configurar este novo diretório, no arquivo catalina.properties, que se encontra no diretório conf do Tomcat. É necessário incluir o caminho para os jars do diretório shared/lib na entrada common.loader, conforme a imagem abaixo:

Essa é a principal configuração. Realmente é simples, desde que se saiba dos detalhes.

Adicionalmente, há outros detalhes importantes:

Se você estiver executando os dois servidores na mesma máquina é preciso alterar a porta HTTP de um deles. Para alterar a configuração da porta no Tomcat para 8082, por exemplo, edite o arquivo server.xml do diretório conf:

Isto já evitará problema de conflito de portas.

Uma outra dica que deixo, agora com relação à configuração da aplicação JSF e suas bibliotecas, é a seguinte: deixe no diretório lib do Tomcat apenas os jars do Mojarra (jsf-api.jar, jsf-impl.jar) e o jstl.jar. As bibliotecas do Richfaces (richfaces-api-3.3.2.GA.jar, richfaces-impl-3.3.2.GA.jar e richfaces-ui-3.3.2.GA.jar) devem ser mantidas no diretório lib da aplicação.

Localizando os EJBs Remotos


Para fazer a localização dos EJBs remotos na aplicação web, deixo como sugestão este método, para se obter o InitialContext:

Observe as duas propriedades comentadas. Por padrão os listeners de IIOP do GlassFish aceitam conexões em localhost e na porta 3700, e não é necessário informar isto caso você esteja rodando os dois servidores na mesma máquina. Caso precise rodar o GlassFish em outra máquina, informe alí naquelas duas propriedades o host e a porta para acessar o GlassFish.

GlassFish em outro servidor

Caso você precise instalar o GlassFish em um servidor diferente, será preciso habilitar o  mesmo para ouvir conexões IIOP no IP do servidor. Para isso é recomendado adicionar uma nova configuração de Listener IIOP, além da que já está configurada por padrão (que está disponível para localhsot e porta 3700). Para fazer isto acesse o seguinte menu, no console de administração do GlassFish:

Na tela que se abre cadastre uma nova configuração de listener, igual a configuração padrão (orb-listener-1), mas alterando o host e a porta. A imagem abaixo mostra uma nova configuração em destaque:

Bom, espero que esta dica ajude a quem está passando por problemas com esta configuração. Como disse procurei não entrar em detalhes de implementação dos projetos. A única dica que deixo é que para Session Bean Locais a injeção de dependência não vai funcionar no GlassFish, ou seja, referências a estes Session Beans anotados com @EJB não funcionarão. É preciso usar interfaces remotas.

Está dada a dica!