Вы находитесь на странице: 1из 24

JSF: Controle de Sesses e Controle de Acesso

Neste artigo, veremos como implementar dois recursos importantes em solues web baseadas em Java EE e, mais especificamente, em JSF: o controle de sesses e de acesso a recursos e funcionalidades de acordo com o nvel de permisso do usurio logado.
Sesses e Controle de Acesso Neste artigo, veremos como implementar dois recursos bastante importantes em solues web baseadas em Java EE e, mais especificamente, em JavaServer Faces: o controle de sesses e o controle de acesso a recursos e funcionalidades de acordo com o nvel de permisso do usurio logado. Para isso, tomaremos como base uma aplicao cujo tema foi escolhido a dedo, aproveitando esta poca de alta temporada em pleno vero brasileiro: um servio de aluguel de chals em Ubatuba, litoral norte do estado de So Paulo.

Em que situao o tema til Este tema ser til sempre que o leitor, profissional de arquitetura e/ou desenvolvimento de software, optar pela plataforma Java EE, pela especificao JavaServer Faces e por sua implementao mais popular, o PrimeFaces, para a construo de aplicaes web que envolvem o acesso de mltiplos usurios controlados atravs de sesses e com variados nveis de acesso s funcionalidades disponveis. Aplicaes web so constitudas, normalmente, de recursos e funcionalidades pblicos, acessveis a toda pessoa em contato com a Internet, e outros protegidos por nveis especficos de acesso. muito comum vermos, em portais pela web, reas restritas para candidatos em treinamento, funcionrios, professores, administradores de sistemas, exibidas apenas mediante o fornecimento de credenciais com nvel de acesso compatvel com aquele previamente cadastrado em sistemas de informao relacionados. Outro comportamento muito comum em sistemas web a exibio de determinadas informaes sobre as quais o leque de operaes permitidas varia de acordo com o tipo de usurio. Para exemplificar, imagine um sistema hipottico de controle acadmico: enquanto professores possuem um perfil que os permite digitar as notas de seus alunos, estes ltimos possuem um perfil um pouco mais restrito, que s os d acesso visualizao de suas prprias notas. Embora a informao (nota) seja visvel para ambos os perfis de usurio (professores e alunos), o escopo de atuao sobre ela varia sensivelmente. H trs caractersticas importantes no enredo acima: Determinadas operaes, para serem apresentadas e utilizadas, exigem autenticao de usurios; As atividades de um usurio autenticado pertencem a um contexto particular de uso do sistema; Pode haver atividades, em um sistema, que s devem ser apresentadas a tipos especficos de usurio. Todas elas sero abordadas ao longo deste artigo, na forma de um tutorial e uma aplicao tema. Para efeito de norteamento do leitor, trabalharemos com as premissas estabelecidas a seguir: primeira caracterstica da lista acima, ser atribudo o termo controle de acesso; segunda, atribuiremos os termos sesso e controle de sesso; Por fim, terceira caracterstica, atribuiremos o termo controle de perfis de acesso.

Quando iniciarmos o tutorial, mais adiante, tais premissas ajudaro na identificao dos conceitos a elas associados e seu respectivo tratamento dentro das tecnologias adotadas. E j que falamos em tecnologia, vamos analisar os fatos sob esta tica a partir de agora. O mercado nos oferece opes variadas para atingirmos um nvel satisfatrio de segurana em sistemas baseados na plataforma Java EE e tambm fora dela. Boa parte de todo o trabalho consiste na configurao de bibliotecas, frameworks e outros recursos/sistemas relacionados, restando bem pouco a se fazer em termos de implementao. Isto torna as coisas bem mais simples e geis, mas o fato que nem sempre foi assim. Toda esta facilidade de hoje possvel principalmente devido a alguns marcos muito significativos, dos quais destacaremos dois: Lanamento do Java 5 com amplo suporte a anotaes, tornando boa parte da configurao de sistemas, de standalone a web (passando inclusive por mobile), muito mais simples; Lanamento do Java EE 6, com destaque para a API de Servlets 3.0 e CDI, permitindo a configurao de listeners e filtros (e servlets tambm, naturalmente) a partir de anotaes muito simples de compreender e utilizar. Alm desses episdios essenciais da histria da plataforma Java, importante destacarmos a evoluo expressiva do framework Spring, especialmente do mdulo Spring Security, com poderosos recursos para controle de acesso, filtros, redirecionamento, sesses, dentre outros. Trata-se de um mdulo relativamente simples de se usar e muito eficaz dentro de seus propsitos, permitindo ao desenvolvedor incorporar em suas aplicaes um alto grau de robustez e confiabilidade. Atualmente, este mdulo um dos mais maduros do Spring e tem sido aplicado em solues de inmeras empresas ao redor do mundo. Para mais informaes sobre o Spring e, especificamente, o Spring Security, consulte a seo Links ao final do artigo. No tutorial a seguir, veremos como desenvolver uma aplicao em Java para a Web usando apenas os recursos disponveis na plataforma Java EE e, principalmente, na especificao JSF 2 e em sua implementao mais popular, o PrimeFaces. A motivao para a definio deste escopo sugerir ao leitor uma reflexo acerca de uma prtica de mercado muito comum nos dias de hoje, que classificaremos neste artigo como subutilizao. Frameworks so a grande onda da programao orientada a componentes, mas a alta variedade de peas neste jogo tem trazido um efeito colateral preocupante: a combinao de muitos frameworks para atender uma necessidade que, muitas vezes, seria sanada apenas por um ou dois deles. O profissional dos dias de hoje conhece bem pouco o material com que trabalha em seu dia a dia, e algumas combinaes de tecnologias so frequentemente utilizadas meramente por terem sido, em algum momento, rotuladas no mercado como garantias absolutas e inquestionveis de qualidade, disponibilidade, escalabilidade e segurana. Ser mesmo? Ser que, ao longo do tempo, a necessidade por agilidade no nos tem trazido certa pressa pelo resultado, gerando deficincias e fragilidades para as quais temos dado, erradamente, pouca ateno e importncia? Hoje observamos o Spring como padro consolidado de mercado. No que haja algum problema com este framework, embora seu uso possa ser at substitudo, em alguns casos, com recursos que o prprio Java EE 6 passou a oferecer (como o j mencionado CDI, acrnimo para Context and Dependency Injection). Para integrao entre sistemas, por exemplo, podemos fazer uso do Spring Integration, mas existem alternativas bem interessantes e muito poderosas igualmente fceis de aprender e empregar, como o Apache Camel, da Fundao Apache (vide seo Links para referncia a este projeto). A reflexo que estamos sugerindo para o leitor da Java Magazine, principalmente para aqueles que j trabalham com (ou se interessam por) desenvolvimento web, : e se, um dia, Spring no fosse mais uma opo? Como as empresas e os profissionais reagiriam a um movimento como este? A preocupao maior deste texto que segue exatamente esta: trazer para o leitor uma proposta, de certa forma, minimalista, que explore em uma intensidade maior os recursos de uma especificao slida como a Java EE, como alternativa para essas combinaes mercadolgicas frequentemente encontradas por a. Estas combinaes, normalmente, configuram um uso muito aqum do potencial que cada framework empregado

possui, gerando normalmente um indesejvel (e desnecessrio) overhead com reflexos diretos no desempenho e na complexidade de sistemas, alm de exigir, naturalmente, profissionais gabaritados em uma gama muito maior de tecnologias do que o realmente necessrio para se colocar aplicaes de qualidade em execuo. A aplicao exemplo A aplicao desenvolvida para este artigo engloba a exibio das acomodaes de chals localizados em uma praia de Ubatuba, litoral norte do estado de So Paulo. Todos os recursos da aplicao esto protegidos e requerem autenticao do usurio, que pode ser de trs tipos. A Tabela 1 descreve brevemente cada um deles, informando seus respectivos nveis de acesso aos recursos oferecidos pelo sistema.

Tabela 1. Tipos de usurio e seus nveis de acesso. Trabalhando com sesses de usurio Sempre que precisarmos registrar as operaes realizadas por um usurio em uma aplicao web e, principalmente, manter dados produzidos e/ou consumidos em memria ao longo do perodo em que este usurio estiver em atividade, estar usando o que se entende, em computao, por sesso. Uma sesso pode ser assim definida: uma troca de informaes semipermanente, interativa, ou ainda: um dilogo, uma conversao ou reunio entre dois ou mais dispositivos ou um computador e um usurio. Observe a Figura 1. Esta a pgina de entrada do sistema. H trs caractersticas que devemos garantir na implementao deste sistema, referentes a esta pgina inicial: 1. A execuo do sistema deve iniciar pela apresentao desta pgina; 2. Caso as credenciais oferecidas no correspondam a um usurio vlido, o sistema deve permanecer apresentando esta pgina; 3. O encerramento do uso (logout) deve resultar na apresentao desta pgina, com o respectivo encerramento da sesso do usurio. Observe agora a Figura 2, que nos mostra a estrutura do projeto. Em conjunto com a Figura 1, h ainda uma terceira caracterstica importante que devemos garantir que esteja coberta por nossa implementao: 1. No havendo uma sesso estabelecida para o usurio, a nica pgina que pode ser apresentada para o usurio ser a pgina inicial. Portanto, a digitao direta de qualquer endereo, referente a qualquer pgina do sistema, deve ser sempre filtrada e verificada pelo sistema.

abrir imagem em nova janela Figura 1. Tela de login da aplicao. O cdigo-fonte da pgina apresentada na Figura 1 pode ser observado na Listagem 1. A ao de entrada no sistema processada a partir de um bean gerenciado, com escopo de sesso, chamado MbLogin e identificado, na pgina, pela varivel mbLogin. O cdigo-fonte desta classe pode ser visto na Listagem 2.

Figura 2. Estrutura do projeto DevmediaApp. Listagem 1. Pgina de login da aplicao.


<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui" > <h:head> <title>#{messages.LOGIN_TITULO}</title> <h:outputStylesheet name="estilos.css" library="css" /> <style type="text/css"> .ui-widget{font-size:90% !important;} </style> </h:head> <h:body style="background-image:url ('resources/images/summer-background-wallpaper.jpg'); background-color:transparent;"> <div id="tudo" align="center" style="background-color:transparent;"> <h:form style="background-color:transparent;"> <p:panel style="background-color: transparent;"> <p:panelGrid columns="2" style="background-color: transparent;" > <h:outputLabel value="#{messages.LOGIN_USERNAME}" styleClass="required_labels" /> <p:inputText value="#{mbLogin.usuario.username}" size="45" id="campo_usuario" /> <h:outputLabel value="#{messages.LOGIN_SENHA}" styleClass="required_labels" goodLabel="#{messages.LOGIN_SENHA_MEDIA}" /> <p:password value="#{mbLogin.usuario.senha}" maxlength="20" weakLabel="#{messages.LOGIN_SENHA_FRACA}" strongLabel="#{messages.LOGIN_SENHA_FORTE}" id="campo_senha" feedback="true" size="45" /> </p:panelGrid> <p:commandButton ajax="false" value="#{messages.LOGIN_COMANDO_ENTRAR}" styleClass="comandos" action="#{mbLogin.doLogin()}" /> <p:commandButton ajax="true" value="#{messages.LOGIN_COMANDO_LIMPAR}" styleClass="comandos" action="#{mbLogin.limparTela()}" update="campo_usuario campo_senha" /> </p:panel> </h:form>

</div> </h:body> </html>

Listagem 2. Bean gerenciado para operaes de login, logout e controle de sesses.


/** * Bean responsvel por controlar operaes de login, logout e controle de * sesses de usurios. */ @ManagedBean @SessionScoped public class MbLogin implements Serializable { private static final String LOGIN_SUCESSO = "login_sucesso"; public static final String LOGIN_FALHA = "login_falha"; public static final String SESSAO_INEXISTENTE = "sessao_invalida"; private static final String OUTCOME_LOGOUT = "logout"; public static final String USUARIO_SESSAO = "usuario"; private Usuario usuario; private ControladorAcesso controladorAcesso; public MbLogin() {} @PostConstruct public void inicializar() { usuario = new Usuario(); controladorAcesso = new ControladorAcesso(); Logger.getLogger(MbLogin.class).log(Level.INFO, ">>>>>>>>>>>>> Inicializando um bean de login."); } /** * Utilizado para tentativas de login no sistema, confrontando dados fornecidos * pelo usurio com registros de usurios cadastrados. * * @return Outcome associado a fracasso ou sucesso na tentativa de login. */ public String doLogin() { if (camposPreenchidos() && !isUsuarioLogado()) { if (new ServicoLogin(usuario).executar()) { // Descobrindo o tipo de usurio que est tentando acessar o sistema. Usuario usuarioLogado = new ServicoCarregarUsuario(). carregarDados(usuario.getUsername(), usuario.getSenha()).get(0); usuarioLogado.setStatus(Usuario.ATIVO);

HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance(). getExternalContext().getSession(true); sessao.setAttribute(USUARIO_SESSAO, usuarioLogado); controladorAcesso.configurarAcesso(); // Atualizando sistema de informao para informar que o usurio est logado. new ServicoAtivarUsuario(usuarioLogado).cadastrar(); return LOGIN_SUCESSO; } } return LOGIN_FALHA; } /** * Utilizado para finalizar uma sesso de um usurio no sistema. * * @return Outcome associado a fracasso ou sucesso na tentativa de logout. */ public String doLogout() { FacesContext context = FacesContext.getCurrentInstance(); HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance(). getExternalContext().getSession(false); Usuario usuarioSessao = (Usuario) sessao.getAttribute(USUARIO_SESSAO); if (usuarioSessao != null) { usuarioSessao.setStatus(Usuario.INATIVO); new ServicoDesativarUsuario(usuarioSessao).cadastrar(); } context.getExternalContext().invalidateSession(); return OUTCOME_LOGOUT; } /** * Utilizado para verificar se as credenciais necessrias para realizao do * login foram preenchidas. * * @return * */ private boolean camposPreenchidos() { return (usuario != null && usuario.getUsername() != null && !"".equals(usuario.getUsername()) && usuario.getSenha() != null && !"".equals(usuario.getSenha())); } <code>true</code> em caso de dados preenchidos. <code>false</code> caso contrrio.

/** * Mtodo utilizado para verificar se um usurio tentando logar na aplicao * j no possui alguma sesso aberta em outro navegador ou outra aba. A * aplicao est barrando mltiplos acessos simultneos de um usurio. * * @return <code>true</code> se j existir uma sesso ativa para o usurio. * */ private boolean isUsuarioLogado() { return new ServicoControleSessao(usuario).executar(); } /** * Limpa todos os dados da tela de login. */ public void limparTela() { this.usuario = new Usuario(); } public Usuario getUsuario() { return usuario; } public void setUsuario(Usuario usuario) { this.usuario = usuario; } public ControladorAcesso getControladorAcesso() { return controladorAcesso; } } <code>false</code> caso contrrio.

Este bean gerenciado possui um mtodo, doLogin(), dentro do qual a operao de entrada acontece. O escopo de sesso de MbLogin garantir que, ao longo de todo o tempo em que uma sesso de usurio esteja vlida e ativa, apenas uma instncia desta classe seja utilizada, o que nos permite utiliz-la para manter determinados estados da aplicao durante este perodo. Este um ponto que merece um pouco de nossa ateno. Beans gerenciados, que so classes cujo ciclo de vida mantido pelo prprio container (que pode ser um servidor de aplicaes como JBoss ou GlassFish, ou web servers como Jetty ou Tomcat), precisam ter o seu escopo de validade definido via configurao ou anotao, e isto o que definir por quanto tempo este objeto ficar em memria e tambm em que momento ser criado. Apenas para relembrarmos, ou apresentarmos caso o leitor ainda no tenha experincia com JSF, o contedo a seguir ser dedicado a uma explicao sucinta sobre cada um dos escopos: Escopo de requisio (request, @RequestScoped): beans gerenciados sob este escopo sero instanciados a cada nova requisio enviada para o servidor, permanecendo em memria at o momento em que a resposta enviada para o cliente. A partir da, este objeto removido da memria, sendo destrudo. Comentaremos, em breve, sobre o ciclo de vida de requisies JSF, cujo diagrama est ilustrado na Figura 3;

Escopo de Sesso (session, @SessionScoped): este escopo atua como um compensador da natureza stateless do protocolo HTTP, sendo utilizado para garantir que um bean gerenciado permanea ativo ao longo de toda uma experincia de uso do aplicativo (por exemplo, do momento do login ao momento do logout), independente do nmero de requisies ocorridas; Escopo de Aplicao (application, @ApplicationScoped): este um escopo ainda mais amplo que o de sesso, pois garante que um bean gerenciado estar disponvel do momento em que a aplicao inicializada at a sua parada. Um bean com escopo de aplicao estar disponvel, dentro do tempo de vida de uma aplicao, para todas as requisies de todas as sesses ativas. O espao de memria que este objeto alocar , portanto, comum a um nmero varivel de agentes, e a escolha deste escopo implica em um cuidado ainda maior na implementao dos recursos que este bean disponibilizar para o sistema. Do JSF 2.0 em diante, todo bean gerenciado com escopo de aplicao pode ser instanciado em dois momentos: quando a aplicao acessada pelo primeiro usurio ou antes da exibio da primeira pgina da aplicao. Neste ltimo caso, existe um atributo chamado eager que pode ser ativado na prpria anotao da classe do bean gerenciado; Escopo de Viso (view): este escopo est presente somente a partir da verso 2.0 da especificao JSF e garante que o bean gerenciado estar vivo em memria enquanto aquela pgina para o qual foi projetado esteja sendo exibida. No momento em que uma nova pgina for apresentada ao usurio, este objeto ser destrudo, pois j no haver mais sentido em mant-lo em memria. Como o leitor pode provavelmente estar pensando a partir desta definio, este um recurso que ajuda bastante no gerenciamento de memria em casos de solicitaes AJAX ao servidor, proporcionando maior dinmica ao sistema como um todo.

Figura 3. Ciclo de vida de uma requisio JSF. Feita esta pausa para uma breve descrio conceitual sobre o ciclo de vida de beans gerenciados do JSF, retomemos a anlise da aplicao-tema deste artigo. Inicialmente, o sistema procura verificar se os campos esto preenchidos (atravs do mtodo camposPreenchidos()), e caso estejam, uma instncia do servio de login criada para verificar a validade do usurio junto ao banco de dados da aplicao. Para efeito de ilustrao de recursos do Java EE e do JSF, foi estabelecido um requisito de que no devero existir sesses simultneas para um mesmo usurio. Para tornar isto possvel, foi definido em banco de dados, para a tabela de usurios, uma flag de controle que guarda exatamente a informao de sesso ativa. Usamos a Tabela 2 para descrever como esta tabela de usurios foi projetada no banco de dados.

A lgica para garantir este comportamento se encontra no mtodo isUsuarioLogado(). Nele, instanciamos um servio de controle de sesso (classe ServicoControleSesso), que verificar se o registro deste usurio, no banco de dados, est com sua flag de sesso desligada.

Tabela 2. Estrutura da tabela de usurios no banco de dados. Caso o usurio no esteja logado em nenhum outro navegador, uma nova sesso ser criada e esta instncia de usurio ser, nela, gravada. Manter os dados do usurio na instncia que representa a sesso de uso permitir, por exemplo, recuper-los de qualquer outro ponto da aplicao, durante todo o tempo em que esta sesso estiver ativa, aberta. A classe central da especificao JSF a javax.faces.context.FacesContext. Por ela, conseguimos obter referncias a elementos fundamentais de toda a especificao, como a aplicao, lista de mensagens, kit de renderizao e a raiz da rvore de componentes visuais (view root). Atravs deste objeto de contexto, podemos tambm recuperar uma instncia da classe ExternalContext (javax.faces.context.ExternalContext), atravs da qual, ento, conseguimos obter uma referncia para a sesso atual. O parmetro booleano passado para o mtodo getSession() desta classe indica que, caso no exista uma sesso ativa, uma nova ser criada. A documentao completa, com todos os seus detalhes, pode ser encontrada na documentao oficial da Oracle (vide seo Links). Assim que as credenciais de um usurio forem validadas pela aplicao, e sendo este um usurio conhecido, estabelecer-se- uma sesso para ele, vlida do momento da operao de login at a respectiva operao de logout. Ao longo deste perodo de tempo, todas as atividades que o usurio realizar estaro vinculadas a esta sesso, este contexto particular e conhecido de uso da aplicao. Sesses, em Java, so representadas pela abstrao HttpSession, do pacote javax.servlet.http, e sua principal utilidade garantir a manuteno do estado de uma aplicao durante um contexto particular de uso, uma vez que o protocolo HTTP, stateless por definio, no nos oferece este recurso. Para facilitar a compreenso do conceito de sesso, imagine um carrinho de compras em portais como Submarino ou Balo da Informtica. Enquanto navegamos, temos a possibilidade de adicionar itens ao nosso carrinho, que permanece com essas informaes independentemente de quantas pesquisas faamos ou quantas pginas visitemos ao longo da experincia de uso. O contexto de uso aqui representado pelo intervalo de tempo iniciado quando o usurio entra no portal at aquele em que, tendo feito todas as suas compras, resolve encerrar a utilizao dele. Como antecipamos no texto, o sistema ter tambm uma operao de logout. Esta operao deve estar disponvel em todas as pginas, e para isto utilizamos o recurso de templates do JSF, atravs da

implementao oferecida pelo PrimeFaces. Observe a Listagem 3, que contm esta estrutura bsica das pginas da soluo. Listagem 3. Template das pginas da aplicao.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns=http://www.w3.org/1999/xhtml xmlns:h=http://java.sun.com/jsf/html xmlns:f=http://java.sun.com/jsf/core xmlns:p="http://primefaces.org/ui" xmlns:ui="http://java.sun.com/jsf/facelets" > <h:head> <title>#{messages.APLICACAO_TITULO}</title> <style type="text/css"> .ui-widget{font-size:90% !important;} </style> </h:head> <h:body style="background-image:url ('resources/images/summer-background-wallpaper.jpg'); background-color:transparent;"> <h:form> <p:layout fullPage="true"> <p:layoutUnit position="north" size="100" resizable="false" closable="false" collapsible="false" > <div align="center"> <font 60px; font-weight: bolder;"> #{messages.HOME_TITULO} </font> </div> </p:layoutUnit> <p:layoutUnit position="south" size="50" resizable="false" closable="false" collapsible="false" > <div align="center"> <h:outputText style="text-align: center;" value="#{messages.TEMPLATE_RODAPE}" /> </div> </p:layoutUnit> <p:layoutUnit position="west" size="200" header="#{labels.menu_opcoes}" resizable="false" closable="false" collapsible="false"> <p:commandButton action="/home.xhtml" ajax="false" style="width: 180px; margin-left: 5px; style="font-family: Verdana, sans-serif; color: green; font-size:

margin-right: /> <br />

5px;"

value="#{messages.TEMPLATE_MENU_INFO}"

<p:commandButton action="/praia.xhtml" style="width: 180px; margin-left: 5px; margin-right: 5px;" value="#{messages.TEMPLATE_MENU_FOTOS_PRAIA}" ajax="false" <p:commandButton action="/chales.xhtml" style="width: 180px; margin-left: 5px; margin-right: 5px;" value="#{messages.TEMPLATE_MENU_FOTOS_CHALES}" ajax="false" /> <p:commandButton action="/admin.xhtml" style="width: 180px; margin-left: 5px; margin-right: 5px;" value="#{messages.TEMPLATE_MENU_AREA_ADMIN}" ajax="false" rendered="#{mbLogin.controladorAcesso.permissaoFuncionario}" /> <br /> <p:commandButton action="#{mbLogin.doLogout()}" style="width: 180px; margin-left: 5px; margin-right: 5px; background-color:crimson; color: white; font-weight: bolder; " value="#{messages.TEMPLATE_MENU_LOGOUT}" ajax="false" /> </p:layoutUnit> <p:layoutUnit position="center" id="template_content" > <ui:insert name="page_content" /> </p:layoutUnit> </p:layout> </h:form> </h:body> </html> <br /> /> <br />

Nela, podemos ver o comando de logout configurado para acionar um mtodo do bean gerenciado MbLogin, responsvel por operaes de login e logout na aplicao. Este mtodo o MbLogin.doLogout(). Inicialmente, a lgica contida nele recupera o contexto da requisio atual, atravs do mtodo esttico FacesContext.getCurrentContext(). A partir da, a instncia de sesso atual obtida, e ocorre uma tentativa de recuperao do objeto que representa o usurio atualmente logado no sistema. Caso exista, os dados deste usurio sero utilizados para atualizar a sua flag de sesso no banco de dados, atravs de uma instncia da classe de servio ServicoDesativarUsuario. Por fim, a sesso invalidada atravs da referncia para a instncia de ExternalContext, j mencionada, e a sada produzida (OUTCOME_LOGOUT) aciona uma regra de navegao do JSF que faz com que o sistema apresente a pgina de login para o usurio. O recurso de autenticao fica, assim, completo. Entretanto, outro ponto que merece destaque e precisa estar coberto em nosso sistema o controle do acesso ao sistema por parte de usurios que, porventura, conheam a estrutura bsica do projeto (a qual, para este nosso exemplo, pode ser vista na Figura 2). Embora o sistema esteja configurado para apresentar, em sua inicializao, a pgina de autenticao, qualquer pessoa poderia tentar digitar, diretamente na barra de endereos do navegador, o caminho para qualquer pgina do sistema que no seja a de login. No texto que segue, veremos como possvel filtrar requisies e impedir que este

tipo de navegao seja realizado, salvo por usurios que tenham, previamente, se autenticado junto ao sistema e, consequentemente, iniciado uma sesso de navegao. Sempre que um usurio consumir uma determinada funcionalidade de um sistema em Java para a web, estar enviando pela rede dados encapsulados na forma de uma requisio construda em cima do protocolo HTTP. Para atividades de transformao de dados (cadastros, atualizaes), o mtodo comumente utilizado o POST, enquanto para recuperao de dados apenas para fins de leitura normalmente se utilizam do mtodo GET. A unidade de processamento de requisies web, em Java, conhecida como servlet. O termo let, em computao, normalmente utilizado para representar o diminutivo de um conceito, e servlets, neste sentido, significam pequenos servidores, unidades lgicas que representam o servidor (ou, melhor dizendo, atividades desempenhadas por ele). A API de servlets (mais especificamente a classe javax.servlet.http.HttpServlet) estabelece um mtodo especfico para cada mtodo existente no protocolo HTTP (POST, GET, PUT, DELETE, OPTIONS, HEAD, TRACE). A assinatura desses mtodos segue um padro que inicia com um prefixo do, seguido pelo nome do mtodo em questo. Logo, ao consultar a documentao da API (vide seo Links), o leitor ver mtodos como doPost(), doPut(), doDelete(), doOptions(), dentre outros. No caso do JavaServer Faces, existe um servlet que age como um controlador central, e cuja implementao se encontra na forma de uma classe chamada javax.faces.webapp.FacesServlet. Quando uma requisio chega a este servlet, entra em um ciclo de vida definido dentro da especificao JSF, ilustrado na Figura 3. A requisio percorre esta sequncia de estados e a sinalizao de cada mudana realizada na forma de um evento. O JSF possui uma famlia de classes para representao de eventos, tendo como unidade mais genrica a classe javax.faces.event.FacesEvent. Como sabemos, o modelo de tratamento de eventos do Java estabelece, para cada tipo de evento representado, uma interface com o contrato a ser seguido para o tratamento deste evento, e que normalmente recebe a denominao de listener. A representao mais genrica de listeners para eventos JSF est estabelecida na interface javax.faces.event.FacesListener. Entretanto, para o caso de eventos relacionados a fases de uma requisio JSF, tal hierarquia no foi respeitada. Para representar o evento de fase, temos a classe javax.faces.event.PhaseEvent (uma subclasse direta de java.util.EventObject), e o listener correspondente foi definido atravs da interface javax.faces.event.PhaseListener que, por sua vez, herda outras especificaes de outras duas interfaces utilitrias do Java, java.util.EventListener e java.io.Serializable. Antes que esta ltima afirmao soe um pouco estranha para o leitor, revisitemos os conceitos de orientao a objetos e, mais particularmente, como esses conceitos so implementados em Java. Eles enunciam que, embora no possamos trabalhar com herana mltipla em classes de objetos, podemos t-la, de certo modo, aplicada a interfaces. Logo, perfeitamente normal que uma interface como a PhaseListener estenda de especificaes pr-estabelecidas em outras duas interfaces (contratos, protocolos, como preferir). Conforme j vimos anteriormente neste artigo, desejamos filtrar requisies para verificar, inicialmente, se existe uma sesso ativa para o usurio. Mais que isso, desejamos redirecionar o usurio para a pgina de entrada caso o usurio, ainda no logado, deseje acessar qualquer pgina interna da aplicao, via barra de endereos do navegador. Tudo comea atravs da implementao de um listener de fase, que pode ser visto na Listagem 4. O primeiro ponto importante a ressaltar que o mtodo PhaseListener.getPhaseId() implementado para retornar sempre a instncia que representa a fase RESTORE_VIEW (observe novamente a Figura 3, e note que este o primeiro estado em que a requisio entra). Isto faz com que apenas eventos relacionados a esta fase sejam capturados, ignorando-se todos os demais eventos das fases subsequentes de uma requisio. J no momento posterior ao da fase, o que esta classe faz identificar, atravs da instncia de contexto correspondente a esta requisio (FacesContext), se a pgina que se deseja acessar a de login. Caso no

seja, o prximo passo verificar se, no objeto que representa a sesso, existe um usurio salvo (o que configura, portanto, uma sesso ativa). Caso exista, permite-se a navegao desejada, e em caso contrrio, estabelece-se programaticamente um redirecionamento para a pgina de login atravs de uma instncia de javax.faces.application.NavigationHandler. O mtodo usado para isto o handleNavigation(), em que so passados como parmetros o contexto da requisio atual, uma ao e um outcome. Navegao em JSF trabalha tanto com outcomes quanto com aes. Atravs de configurao, podemos definir quais aes e resultados devem ser respeitados para que a aplicao navegue de uma pgina a outra. Estas configuraes podem ser observadas na Listagem 5. Nesta mesma listagem, podemos ver como se configura um listener de fases para uma aplicao em Java para a web, atravs de um marcador <phaselistener>, dentro do contexto de ciclo de vida da aplicao (representado pelo n <lifecycle>). O cdigo correspondente ao listener, que j discutimos acima, pode ser consultado em detalhes na Listagem 4. Para testar o comportamento do sistema, basta que executemos a aplicao e, ainda antes de executar o login com um usurio vlido, digitar o endereo completo de qualquer pgina existente no projeto direto na barra de endereos do navegador web. Como, neste momento, ainda no existe uma sesso ativa, o sistema redirecionar o usurio para a pgina de entrada. Listagem 4. Listener de autenticao .
/** * Responsvel por manipular requisies de usurio, permitindo acesso ao * contedo da aplicao somente no caso do usurio j ter se autenticado. */ public class ListenerAutenticacao implements PhaseListener { private static final String PAGINA_LOGIN = "index.xhtml"; @Override public void afterPhase(PhaseEvent event) { FacesContext contexto = event.getFacesContext(); String pagina = contexto.getViewRoot().getViewId(); System.out.println(">>>>>>>>>>>>>>>>> ListenerAutenticacao.afterPhase() " + "para pgina de ID " + event.getFacesContext() .getViewRoot().getViewId()); if (!(pagina.lastIndexOf(PAGINA_LOGIN) > -1)) { HttpSession sessao = (HttpSession) contexto.getExternalContext().getSession(false); Object usuario = sessao.getAttribute(MbLogin.USUARIO_SESSAO); if (usuario == null) { NavigationHandler navHandler = contexto.getApplication().getNavigationHandler(); navHandler.handleNavigation(contexto, null, MbLogin.SESSAO_INEXISTENTE); } } }

@Override public void beforePhase(PhaseEvent event) { if (event.getFacesContext().getViewRoot() != null) { System.out.println(">>>>>>>>>>>>>>>>> ListenerAutenticacao.beforePhase() " + "para pgina de ID " + event.getFacesContext().getViewRoot().getViewId()); } else { System.out.println(">>>>>>>>>>>>>>>>> ListenerAutenticacao.beforePhase() " + "indicando view root ainda nula"); } } @Override public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }

Listagem 5. Arquivo de configurao do JSF.

<?xml version='1.0' encoding='UTF-8'?> <faces-config version="2.1" 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-facesconfig_2_1.xsd"> <application> <resource-bundle> <base-name>br.com.devmedia.javamagazine.labels</base-name> <var>messages</var> </resource-bundle> </application> <navigation-rule> <description>Regra de navegao para operao de login </description> <from-view-id>/index.xhtml</from-view-id> <navigation-case> <from-outcome>login_sucesso</from-outcome> <to-view-id>/home.xhtml</to-view-id> <redirect /> </navigation-case> <navigation-case> <from-outcome>login_falha</from-outcome> <to-view-id>/index.xhtml</to-view-id>

<redirect /> </navigation-case> </navigation-rule> <navigation-rule> <description>Regra de navegao para operao de logout </description> <from-view-id>/*</from-view-id> <navigation-case> <from-outcome>logout</from-outcome> <to-view-id>/index.xhtml</to-view-id> <redirect /> </navigation-case> </navigation-rule> <navigation-rule> <description>Regra de navegao para sesses inexistentes </description> <from-view-id>/*</from-view-id> <navigation-case> <from-outcome>sessao_invalida</from-outcome> <to-view-id>/index.xhtml</to-view-id> <redirect /> </navigation-case> </navigation-rule> <lifecycle> <phase-listener>br.com.devmedia.javamagazine.controller. ListenerAutenticacao</phase-listener> </lifecycle> </faces-config>

O controle de permisso de acesso Uma vez cobertos todos os conceitos relevantes sobre sesses de usurio, vamos partir agora para a configurao de um modelo de controle de acesso a funcionalidades de acordo com perfis pr-estabelecidos. De acordo com a Tabela 1 j apresentada, temos trs perfis de acesso pr-configurados no banco de dados. Estes perfis sero utilizados para, em tempo de execuo, configurar o perfil de acesso do usurio logado. Para isto, o projeto contm uma classe chamada ControladorAcesso, que pode ser observada na Listagem 6. Se voltarmos para a Listagem 2, notaremos que existe uma instncia desta classe mantida pelo bean gerenciado responsvel pelas atividades de login e logout. Para o controle de acesso propriamente dito, foram criados trs atributos booleanos que so valorados de acordo com a natureza do usurio que realizou o login. Observe, novamente, a Tabela 2, e ver que existe uma coluna na tabela de usurios, chamada tipo, que guarda exatamente o nvel de permisso de acesso associado a cada usurio. Listagem 6. O controlador de acesso da aplicao.

/** * Controlador utilizado especificamente para verificao de permisses de * acesso a funcionalidades oferecidas pela aplicao. */ public class ControladorAcesso { private boolean permissaoAdministrador; private boolean permissaoFuncionario; private boolean permissaoComum; public boolean isPermissaoAdministrador() { HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance().getExternalContext(). getSession(true); Usuario usuarioSessao = (Usuario) sessao.getAttribute (MbLogin.USUARIO_SESSAO); if (usuarioSessao != null) { permissaoAdministrador Usuario.ADMINISTRADOR); } else { permissaoAdministrador = false; } return permissaoAdministrador; } public boolean isPermissaoFuncionario() { HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance().getExternalContext(). getSession(true); Usuario usuarioSessao = (Usuario) sessao.getAttribute (MbLogin.USUARIO_SESSAO); if (usuarioSessao != null) { permissaoAdministrador Usuario.ADMINISTRADOR); if (permissaoAdministrador) { permissaoFuncionario = true; } else { permissaoFuncionario Usuario.FUNCIONARIO); } } else { permissaoFuncionario = false; } return permissaoFuncionario; } = (usuarioSessao.getTipo() == = (usuarioSessao.getTipo() == = (usuarioSessao.getTipo() ==

public boolean isPermissaoComum() { HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance().getExternalContext(). getSession(true); Usuario usuarioSessao = (Usuario) sessao.getAttribute (MbLogin.USUARIO_SESSAO); if (usuarioSessao != null) { permissaoAdministrador Usuario.ADMINISTRADOR); permissaoFuncionario Usuario.FUNCIONARIO); if (permissaoAdministrador || permissaoFuncionario) { permissaoComum = true; } else { permissaoComum } } else { permissaoComum = false; } return permissaoComum; } /** * Mtodo utilizado para configurar o perfil de acesso do usurio logado s * funcionalidades da aplicao. */ public void configurarAcesso() { HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true); Usuario usuarioSessao = (Usuario) sessao.getAttribute (MbLogin.USUARIO_SESSAO); if (usuarioSessao != null) { Logger.getLogger("ControladorAcesso").log(Level.INFO, ">>>>>>>>>>>>>> usuarioSessao.getTipo()); permissaoAdministrador Usuario.ADMINISTRADOR); if (permissaoAdministrador) { permissaoFuncionario = true; } else { = (usuarioSessao.getTipo() == Usurio da sesso tem tipo {0}", = (usuarioSessao.getTipo() == Usuario.CONVIDADO); = (usuarioSessao.getTipo() == = (usuarioSessao.getTipo() ==

permissaoFuncionario Usuario.FUNCIONARIO); permissaoComum } } } }

= (usuarioSessao.getTipo() ==

= (usuarioSessao.getTipo() ==

Usuario.CONVIDADO);

J sabemos que, assim que a operao de login for realizada com sucesso, ser guardada na sesso uma instncia da classe br.com.devmedia.javamagazine.domain.Usuario, representando o usurio que est acessando o sistema. Deste modo, sempre que necessrio, a aplicao determinar o nvel de permisso de acesso s suas funcionalidades de acordo com esta instncia. A lgica desta unidade de controle utilizar as premissas listadas a seguir para determinar o nvel de acesso s funcionalidades: Se o nvel do usurio for CONVIDADO, apenas o atributo booleano permissaoComum ser valorado em true; Se o nvel de usurio for FUNCIONRIO, os atributos booleanos permissaoComum e permissaoFuncionario sero valorados em true. Se o nvel de usurio for ADMINISTRADOR, todos os trs atributos booleanos (permissaoAdministrador, permissaoFuncionario e permissaoComum) sero valoradas em true. Esses atributos foram definidos como booleanos para que o recurso de renderizao de componentes do PrimeFaces possa ser aproveitado. Existe uma propriedade, comum a todo componente visual do PrimeFaces, chamado rendered. Quando definido em true, que o valor padro para esta propriedade, a mquina de renderizao trabalhar no desenho deste componente. Caso contrrio, este componente ser escondido, e a pgina se ajustar para exibir apenas os demais itens, classificados como renderizveis. Para exemplificar este recurso, vamos analisar duas pginas do sistema: uma delas contm funcionalidades que so visveis apenas a funcionrios e outras visveis apenas para administradores, e a segunda pgina o template j apresentado na Listagem 3. Iniciaremos pelo template. Se voltarmos a esta listagem, verificaremos que a pgina de administrao (admin.xhtml) tem a sua apresentao controlada a partir desta propriedade, rendered, da seguinte maneira: rendered="#{mbLogin.controladorAcesso.permissaoFuncionario}" J a pgina de administrao, cujo contedo se encontra na Listagem 7, dividida em duas partes: uma de visualizao de dados de pacotes de viagem e outra de cadastro de novos pacotes. Enquanto a visualizao de pacotes permitida tanto a funcionrios comuns quanto a administradores, somente estes ltimos podero visualizar e manipular a seo de cadastro de novos pacotes. Esta configurao feita da seguinte maneira: rendered="#{mbLogin.controladorAcesso.permissaoAdministrador}" Desta maneira, podemos filtrar quais funcionalidades estaro visveis a cada tipo de usurio, e todo o controle realizado programaticamente, eventualmente envolvendo sistemas de informao como bancos de dados, dependendo sempre da soluo em questo. O sistema, que estar disponvel para download atravs do portal da DevMedia, contm outros exemplos de controle de acesso e alguns trechos adicionais envolvendo o controle de sesses, dando a oportunidade do leitor de explorar um pouco mais cada um desses recursos. Listagem 7. Pgina destinada a funcionrios e administradores da aplicao.

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>#{messages.PRAIAS_TITULO}</title> </h:head> <h:body> <h:form> <ui:composition template="templates/template.xhtml"> <ui:define name="page_content"> <p:accordionPanel> <p:tab title="#{messages.ADMIN_ACCORDION_TITULO_PRECOS}"> <h:panelGrid columns="1" cellpadding="10"> <p:dataTable id="tabelaPrecos" var="diaria" value="#{mbAluguel.diariasReferencia}" paginator="true" rows="10" rowsPerPageTemplate="5,10,15" paginatorTemplate="{CurrentPageReport} {LastPageLink} {RowsPerPageDropdown}" > <f:facet name="header"> #{messages.ADMIN_ACCORDION_TITULO_PRECOS} </f:facet> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_TABELA_COLUNA_MES_REFERENCIA}" /> </f:facet> <h:outputText value="#{diaria.mesReferencia}" /> </p:column> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_TABELA_COLUNA_ANO_REFERENCIA}" /> </f:facet> <h:outputText value="#{diaria.anoReferencia}" /> {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink}

</p:column> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_TABELA_COLUNA_DIARIA}" /> </f:facet> <h:outputText value="#{diaria.valor}" /> </p:column> </p:dataTable> </h:panelGrid> </p:tab> <p:tab title="#{messages.ADMIN_ACCORDION_TITULO_PACOTES}"> <h:panelGrid columns="1" cellpadding="10"> <p:dataTable id="tabelaPacotes" var="pacote" value="#{mbAluguel.pacotes}" paginator="true" rows="10" rowsPerPageTemplate="5,10,15" paginatorTemplate="{CurrentPageReport} {LastPageLink} {RowsPerPageDropdown}" > <f:facet name="header"> #{messages.ADMIN_TABELA_PACOTES_2013} </f:facet> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_PACOTE_NOME}" /> </f:facet> <h:outputText value="#{pacote.nome}" /> </p:column> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_PACOTE_DESCRICAO}" /> </f:facet> <h:outputText value="#{pacote.descricao}" /> </p:column> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_PACOTE_DATA_INICIO}" /> </f:facet> <h:outputText value="#{pacote.dataInicio}" /> </p:column> {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink}

<p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_PACOTE_DATA_TERMINO}" /> </f:facet> <h:outputText value="#{pacote.dataFim}" /> </p:column> <p:column> <f:facet name="header"> <h:outputText value="#{messages.ADMIN_PACOTE_VALOR}" /> </f:facet> <h:outputText value="#{pacote.valorFechado}" /> </p:column> </p:dataTable> <!-- Formulrio para cadastro de novos pacotes --> <h:outputLabel value="#{messages.ADMIN_PACOTE_CADASTRO_TITULO}" style="color: darkolivegreen; font-size: 1.7em; font-weight: bold;" rendered="# {mbLogin.controladorAcesso.permissaoAdministrador}" /> <p:spacer rendered="# {mbLogin.controladorAcesso.permissaoAdministrador}" /> <p:panel rendered="#{mbLogin.controladorAcesso.permissaoAdministrador}"> <p:panelGrid columns="2"> <h:outputLabel value="#{messages.ADMIN_PACOTE_NOME}" style="color:darkgreen; font-weight: bold;" /> <p:inputText /> <h:outputLabel value="#{messages.ADMIN_PACOTE_DATA_INICIO}" style="color:darkgreen; font-weight: bold;" /> <p:calendar value="#{mbAluguel.pacoteCadastro.dataInicio}" id="pacoteDataInicio" showOn="button" /> <h:outputLabel value="#{messages.ADMIN_PACOTE_DATA_TERMINO}" style="color:darkgreen; font-weight: bold;" /> <p:calendar value="#{mbAluguel.pacoteCadastro.dataFim}" id="pacoteDataFim" showOn="button" /> <h:outputLabel value="#{messages.ADMIN_PACOTE_VALOR}" style="color:darkgreen; font-weight: bold;" /> <p:inputText value="#{mbAluguel.valorPacoteAvaliacao}" size="40" maxlength="15" id="parcela" /> value="#{mbAluguel.pacoteCadastro.nome}" size="40"

<h:outputLabel value="#{messages.ADMIN_PACOTE_DESCRICAO}" style="color:darkgreen; font-weight: bold;" /> <p:inputText value="#{mbAluguel.pacoteCadastro.descricao}" size="40" /> </p:panelGrid> <p:commandButton value="#{messages.ADMIN_PACOTE_ACAO_SALVAR}" ajax="false" action="#{mbAluguel.salvarPacote()}" update="tabelaPacotes" /> </p:panel> </h:panelGrid> </p:tab> </p:accordionPanel> </ui:define> </ui:composition> </h:form> </h:body> </html>

Concluso Ao longo do artigo, exploramos os recursos de controle de sesses e permisses de acesso usando apenas a especificao Java EE e, mais especificamente, JavaServer Faces com foco em sua implementao mais popular, o PrimeFaces. Como podemos observar, os recursos apresentados so facilmente aplicveis, cabendo ao desenvolvedor apenas a misso de desenhar uma estratgia consistente para refleti-la, depois, em uma implementao adequada. Embora existam inmeras tecnologias capazes de nos trazer um alto nvel de segurana no mercado, principalmente associadas plataforma Java, importante que sempre pensemos em uma combinao que nos traga, mais que facilidade, boa resposta final. Como falamos no incio do artigo, existem combinaes muito populares no cenrio de desenvolvimento de sistemas web que so, normalmente, rotuladas como modelos obrigatrios, imprescindveis, para a entrega de boas solues. Entretanto, sempre importante observar que, ao adicionarmos mais e mais frameworks em um desenho de sistema web, estamos tambm adicionando overhead na comunicao entre esses componentes e, tambm, uma demanda por profissionais gabaritados em todas essas tecnologias, em alto nvel suficiente para responder com rapidez e alta qualidade no suporte a sistemas que atendem, normalmente, grandes empresas. A especificao Java EE vem evoluindo bastante, e importante que desenvolvedores Java para a web conheam muito bem cada um dos recursos disponveis na implementao (Java EE mais genericamente e Mojarra/MyFaces/PrimeFaces no tocante especificao JSF). Servlets 3.0 e CDI so tpicos que todo leitor e amante do Java devem, desde j, procurar conhecer melhor, pois so bem interessantes e trazem uma facilidade no trabalho com esta plataforma. Alm do que j est disponvel para o desenvolvedor, temos tambm boas promessas definidas para a verso 7 e recursos potencialmente interessantes planejados para as especificaes do Java 8 e Java 9, dentre os quais se destaca o j polmico projeto Jigsaw. Empregar recursos da prpria especificao Java ser sempre um bom negcio, e muitos deles ainda so muito pouco conhecidos e explorados pela grande maioria dos desenvolvedores.

Вам также может понравиться