JSF: Controle de Sessões e Controle de Acesso Neste artigo, veremos como implementar dois recursos importantes em soluções web baseadas em Java EE e, mais especificamente, em JSF: o controle de sessões e de acesso a recursos e funcionalidades de acordo com o nível de permissão do usuário logado. Sessões e Controle de Acesso
Neste artigo, veremos como implementar i mplementar dois recursos r ecursos bastante importantes em soluções web baseadas em Java EE e, mais especificamente, em JavaServer Faces: o controle de sessões e o controle de acesso a recursos e funcionalidades de acordo com o nível de permissão do usuário logado. Para isso, tomaremos como base uma aplicação cujo tema foi escolhido a dedo, aproveitando esta época de alta temporada em pleno verão brasileiro: um serviço de aluguel de chalés chalés em Ubatuba, litoral norte do estado de São Paulo. Em que situação 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 especificação especificação JavaServer Faces e por sua implementação mais popular, o PrimeFaces, para a construção de aplicações web que envolvem o acesso de múltiplos usuários controlados através de sessões e com variados níveis de acesso às funcionalidades disponíveis. Aplicações web são constituídas, normalmente, de recursos e funcionalidades públicos, acessíveis a toda pessoa em contato com a Internet, e outros protegidos por níveis específicos de acesso. É muito comum vermos, em portais pela web, áreas restritas para candidatos em treinamento, funcionários, professores, administradores de sistemas, exibidas apenas mediante o fornecimento de credenciais com nível de acesso compatível com aquele previamente cadastrado em sistemas de informação relacionados. Outro comportamento muito comum em sistemas web é a exibição de determinadas informações sobre as quais o leque de operações permitidas varia de acordo com o tipo de usuário. Para exemplificar, imagine um sistema hipotético de controle acadêmico: 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 à visualização de suas próprias notas. Embora a informação (nota) seja visível para ambos os perfis de usuário (professores e alunos), o escopo de atuação sobre ela varia sensivelmente. Há três características importantes no enredo acima: · Determinadas operações, para serem apresentadas e utilizadas, exigem autenticação de usuários; · As atividades de um usuário autenticado pertencem a um contexto particular de uso do sistema; · Pode haver atividades, em um sistema, que só devem ser apresentadas a tipos específicos de usuário. Todas elas serão abordadas ao longo deste artigo, na forma de um tutorial e uma aplicação tema. Para efeito de norteamento do leitor, trabalharemos com as premissas estabelecidas a seguir: · À primeira característica da lista acima, será atribuído o termo “controle de acesso”; · À segunda, atribuiremos os termos “sessão” e “controle de sessão”; · Por fim, à terceira característica, característica, atribuiremos o termo “controle de perfis de acesso”.
Quando iniciarmos o tutorial, mais adiante, tais premissas ajudarão na identificação 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 opções variadas para atingirmos um nível satisfatório de segurança em sistemas baseados na plataforma Java EE e também fora dela. Boa parte de todo o trabalho consiste na configuração de bibliotecas, frameworks e outros recursos/sistemas relacionados, relacionados, restando bem pouco a se fazer em termos de implementação. Isto torna as coisas bem mais simples e ágeis, mas o fato é que nem sempre foi assim. Toda esta facilidade de hoje é possível principalmente devido a alguns marcos muito significativos, dos quais destacaremos dois: · Lançamento do Java 5 com amplo suporte a anotações, tornando boa parte da configuração de sistemas, de standalone a web (passando inclusive por mobile), muito mais simples; · Lançamento do Java EE 6, com destaque para a API de Servlets 3.0 e CDI, permitindo a configuração de listeners e filtros (e servlets também, naturalmente) a partir de anotações muito simples de compreender e utilizar. Além desses episódios essenciais da história da plataforma Java, é importante destacarmos a evolução expressiva do framework Spring, especialmente do módulo Spring Security, com poderosos recursos para controle de acesso, filtros, redirecionamento, sessões, dentre outros. Trata-se de um módulo relativamente simples de se usar e muito eficaz dentro de seus s eus propósitos, permitindo ao desenvolvedor incorporar em suas aplicações um alto grau de robustez e confiabilidade. Atualmente, este módulo é um dos mais maduros do Spring e tem sido aplicado em soluções de inúmeras empresas ao redor do mundo. Para mais informações sobre o Spring e, especificamente, o Spring Security, consulte a seção Links ao final do artigo. No tutorial a seguir, veremos como desenvolver uma aplicação em Java para a Web usando apenas os recursos disponíveis na plataforma Java EE e, principalmente, na especificação JSF 2 e em sua implementação mais popular, o PrimeFaces. A motivação para a definição deste escopo é sugerir ao leitor uma reflexão acerca de uma prática de mercado muito comum nos dias de hoje, que classificaremos neste artigo como subutilização. Frameworks são a grande onda da programação orientada a componentes, mas a alta variedade de “peças” neste “jogo” tem trazido um efeito colateral pr eocupante: pr eocupante: a combinação 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 combinações de tecnologias são frequentemente utilizadas meramente por terem sido, em algum momento, rotuladas no mercado como garantias absolutas e inquestionáveis de qualidade, disponibilidade, escalabilidade e segurança. Será mesmo? Será que, ao longo do tempo, a necessidade por agilidade não nos tem trazido certa pressa pelo resultado, gerando deficiências e fragilidades para as quais temos dado, erradamente, pouca atenção e importância? Hoje observamos o Spring como padrão consolidado de mercado. Não que haja algum problema com este framework, embora seu uso possa ser até substituído, em alguns casos, com recursos r ecursos que o próprio Java EE ontext and D ependency ependency I njection). Para 6 passou a oferecer (como o já mencionado CDI, acrônimo para C ontext integração entre sistemas, por exemplo, podemos fazer uso do Spring Integration, mas existem alternativas bem interessantes e muito poderosas igualmente fáceis de aprender e empregar, como o Apache Camel, da Fundação Apache (vide seção Links para referência a este projeto). A reflexão 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 não fosse mais uma opção? Como as empresas e os profissionais reagiriam reagiriam a um movimento como este? A preocupação 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 especificação sólida como a Java EE, como alternativa para essas combinações mercadológicas frequentemente frequentemente encontradas por aí. Estas combinações, normalmente, configuram um uso muito aquém do potencial que cada framework empregado
possui, gerando normalmente um indesejável (e desnecessário) overhead com reflexos diretos no desempenho e na complexidade de sistemas, além de exigir, naturalmente, profissionais gabaritados em uma gama muito maior de tecnologias do que o realmente necessário para se colocar aplicações de qualidade em execução. A aplicação exemplo A aplicação desenvolvida para este artigo engloba a exibição das acomodações de chalés localizados em uma praia de Ubatuba, litoral norte do estado de São Paulo. Todos os recursos da aplicação estão protegidos e requerem autenticação do usuário, que pode ser de três tipos. A Tabela 1 descreve brevemente cada um deles, informando seus respectivos níveis de acesso aos recursos oferecidos pelo sistema.
Tabela 1. Tipos de usuário e seus níveis de acesso.
Trabalhando com sessões de usuário Sempre que precisarmos registrar as operações realizadas por um usuário em uma aplicação web e, principalmente, manter dados produzidos e/ou consumidos em memória ao longo do período em que este usuário estiver em atividade, estará usando o que se entende, em computação, por sessão. Uma sessão pode ser assim definida: “uma troca de informações semipermanente, interativa”, ou ainda: “um diálogo, uma conversação ou reunião entre dois ou mais dispositivos ou um computador e um usuário”. Observe a Figura 1. Esta é a página de entrada do sistema. Há três características que devemos garantir na implementação deste sistema, referentes a esta página inicial: 1. A execução do sistema deve iniciar pela apresentação desta página; 2. Caso as credenciais oferecidas não correspondam a um usuário válido, o sistema deve permanecer apresentando esta página; 3. O encerramento do uso (logout) deve resultar na apresentação desta página, com o respectivo encerramento da sessão do usuário. Observe agora a Figura 2, que nos mostra a estrutura do projeto. Em conjunto com a Figura 1, há ainda uma terceira característica importante que devemos garantir que esteja coberta por nossa implementação: 1. Não havendo uma sessão estabelecida para o usuário, a única página que pode ser apresentada para o usuário será a página inicial. Portanto, a digitação direta de qualquer endereço, referente a qualquer página do sistema, deve ser sempre filtrada e verificada pelo sistema.
abrir imagem em nova janela Figura 1. Tela de login da
aplicação.
O código-fonte da página apresentada na Figura 1 pode ser observado na Listagem 1. A ação de entrada no sistema é processada a partir de um bean gerenciado, com escopo de sessão, chamado MbLogin e identificado, na página, pela variável mbLogin. O código-fonte desta classe pode ser visto na Listagem 2.
Figura 2. Estrutura do projeto Listagem 1. Página de login
DevmediaApp.
da aplicação.
"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
#{messages.LOGIN_TITULO}
id="campo_senha"
feedback="true" size="45" />
Listagem 2. Bean gerenciado para operações de login, logout e
controle de sessões.
/** * Bean responsável por controlar operações de login, logout e controle de * sessões de usuários. */ @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 usuário com registros de usuários 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 usuário que está tentando acessar o sistema. Usuario usuarioLogado = new ServicoCarregarUsuario(). carregarDados(usuario.getUsername(), usuarioLogado.setStatus(Usuario.ATIVO);
usuario.getSenha()).get(0);
HttpSession sessao = (HttpSession) FacesContext.getCurrentInstance(). getExternalContext().getSession(true); sessao.setAttribute(USUARIO_SESSAO, usuarioLogado); controladorAcesso.configurarAcesso(); // Atualizando sistema de informação para informar que o usuário está logado. new ServicoAtivarUsuario(usuarioLogado).cadastrar(); return LOGIN_SUCESSO; } } return LOGIN_FALHA; } /** * Utilizado para finalizar uma sessão de um usuário 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 necessárias para realização do * login foram preenchidas. * * @return
true
em caso de dados preenchidos.
*
false
caso contrário.
*/ private boolean camposPreenchidos() { return (usuario != null && usuario.getUsername() != null && !"".equals(usuario.getUsername()) && usuario.getSenha() != null && !"".equals(usuario.getSenha())); }
/** * Método utilizado para verificar se um usuário tentando logar na aplicação * já não possui alguma sessão aberta em outro navegador ou outra aba. A * aplicação está barrando múltiplos acessos simultâneos de um usuário. * * @return
true
se já existir uma sessão ativa para o usuário. *
false
caso contrário.
*/ 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; } }
Este bean gerenciado possui um método, doLogin(), dentro do qual a operação de entrada acontece. O escopo de sessão de MbLogin garantirá que, ao longo de todo o tempo em que uma sessão de usuário esteja válida e ativa, apenas uma instância desta classe seja utilizada, o que nos permite utilizá-la para manter determinados estados da aplicação durante este período. Este é um ponto que merece um pouco de nossa atenção. Beans gerenciados, que são classes cujo ciclo de vida é mantido pelo próprio container (que pode ser um servidor de aplicações como JBoss ou GlassFish, ou web servers como Jetty ou Tomcat), precisam ter o seu escopo de “validade” definido via configuração ou anotação, e isto é o que definirá por quanto tempo este objeto ficará em memória e também em que momento será criado. Apenas para relembrarmos, ou apresentarmos caso o leitor ainda não tenha experiência com JSF, o conteúdo a seguir será dedicado a uma explicação sucinta sobre cada um dos escopos: · Escopo de requisição (request, @RequestScoped): beans gerenciados sob este escopo serão instanciados a cada nova requisição enviada para o servidor, permanecendo em memória até o momento em que a resposta é enviada para o cliente. A partir daí, este objeto é removido da memória, sendo destruído. Comentaremos, em breve, sobre o ciclo de vida de requisições JSF, cujo diagrama está ilustrado na Figura 3;
· Escopo de Sessão (session, @SessionScoped): este escopo atua como um compensador da natureza stateless do protocolo HTTP, sendo utilizado para garantir que um bean gerenciado permaneça ativo ao longo de toda uma experiência de uso do aplicativo (por exemplo, do momento do login ao momento do logout), independente do número de requisições ocorridas; · Escopo de Aplicação (application, @ApplicationScoped): este é um escopo ainda mais amplo que o de sessão, pois garante que um bean gerenciado estará disponível do momento em que a aplicação é inicializada até a sua parada. Um bean com escopo de aplicação estará disponível, dentro do tempo de vida de uma aplicação, para todas as requisições de todas as sessões ativas. O espaço de memória que este objeto alocará é, portanto, comum a um número variável de agentes, e a escolha deste escopo implica em um cuidado ainda maior na implementação dos recursos que este bean disponibilizará para o sistema. Do JSF 2.0 em diante, todo bean gerenciado com escopo de aplicação pode ser instanciado em dois momentos: quando a aplicação é acessada pelo primeiro usuário ou antes da exibição da primeira página da aplicação. Neste último caso, existe um atributo chamado eager que pode ser ativado na própria anotação da classe do bean gerenciado; · Escopo de Visão (view): este escopo está presente somente a partir da versão 2.0 da especificação JSF e garante que o bean gerenciado estará vivo em memória enquanto aquela página para o qual foi projetado esteja sendo exibida. No momento em que uma nova página for apresentada ao usuário, este objeto será destruído, pois já não haverá mais sentido em mantê-lo em memória. Como o leitor pode provavelmente estar pensando a partir desta definição, este é um recurso que ajuda bastante no gerenciamento de memória em casos de solicitações AJAX ao servidor, proporcionando maior dinâmica ao sistema como um todo.
Figura 3. Ciclo de vida de
uma requisição JSF.
Feita esta pausa para uma breve descrição conceitual sobre o ciclo de vida de beans gerenciados do JSF, retomemos a análise da aplicação-tema deste artigo. Inicialmente, o sistema procura verificar se os campos estão preenchidos (através do método camposPreenchidos()), e caso estejam, uma instância do serviço de login é criada para verificar a validade do usuário junto ao banco de dados da aplicação. Para efeito de ilustração de recursos do Java EE e do JSF, foi estabelecido um requisito de que não deverão existir sessões simultâneas para um mesmo usuário. Para tornar isto possível, foi definido em banco de dados, para a tabela de usuários, uma flag de controle que guarda exatamente a informação de sessão ativa. Usamos a Tabela 2 para descrever como esta tabela de usuários foi projetada no banco de dados.
A lógica para garantir este comportamento se encontra no método isUsuarioLogado(). Nele, instanciamos um serviço de controle de sessão (classe ServicoControleSessão), que verificará se o registro deste usuário, no banco de dados, está com sua flag de sessão “desligada”.
Tabela 2. Estrutura da tabela de usuários no
banco de dados.
Caso o usuário não esteja logado em nenhum outro navegador, uma nova sessão será criada e esta instância de usuário será, nela, gravada. Manter os dados do usuário na instância que representa a sessão de uso permitirá, por exemplo, recuperá-los de qualquer outro ponto da aplicação, durante todo o tempo em que esta sessão estiver ativa, aberta. A classe central da especificação JSF é a javax.faces.context.FacesContext. Por ela, conseguimos obter referências a elementos fundamentais de toda a especificação, como a aplicação, lista de mensagens, kit de renderização e a raiz da árvore de componentes visuais (view root). Através deste objeto de contexto, ExternalContext podemos também recuperar uma instância da classe ( javax.faces.context.ExternalContext), através da qual, então, conseguimos obter uma referência para a sessão atual. O parâmetro booleano passado para o método getSession() desta classe indica que, caso não exista uma sessão ativa, uma nova será criada. A documentação completa, com todos os seus detalhes, pode ser encontrada na documentação oficial da Oracle (vide seção Links). Assim que as credenciais de um usuário forem validadas pela aplicação, e sendo este um usuário conhecido, estabelecer-se-á uma sessão para ele, válida do momento da operação de login até a respectiva operação de logout. Ao longo deste período de tempo, todas as atividades que o usuário realizar estarão vinculadas a esta sessão, este contexto particular e conhecido de uso da aplicação. Sessões, em Java, são representadas pela abstração HttpSession, do pacote javax.servlet.http, e sua principal utilidade é garantir a manutenção do estado de uma aplicação durante um contexto particular de uso, uma vez que o protocolo HTTP, stateless por definição, não nos oferece este recurso. Para facilitar a compreensão do conceito de sessão, imagine um carrinho de compras em portais como Submarino ou Balão da Informática. Enquanto navegamos, temos a possibilidade de adicionar itens ao nosso carrinho, que permanece com essas informações independentemente de quantas pesquisas façamos ou quantas páginas visitemos ao longo da experiência de uso. O contexto de uso aqui é representado pelo intervalo de tempo iniciado quando o usuário entra no portal até aquele em que, tendo feito todas as suas compras, resolve encerrar a utilização dele. Como antecipamos no texto, o sistema terá também uma operação de logout. Esta operação deve estar disponível em todas as páginas, e para isto utilizamos o recurso de templates do JSF, através da
implementação oferecida pelo PrimeFaces. Observe a Listagem 3, que contém esta estrutura básica das páginas da solução. Listagem 3. Template das páginas da aplicação.
xmlns:p="http://primefaces.org/ui" xmlns:ui="http://java.sun.com/jsf/facelets" >
Nela, podemos ver o comando de logout configurado para acionar um método do bean gerenciado MbLogin, responsável por operações de login e logout na aplicação. Este método é o MbLogin.doLogout(). Inicialmente, a lógica contida nele recupera o contexto da requisição atual, através do método estático FacesContext.getCurrentContext(). A partir daí, a instância de sessão atual é obtida, e ocorre uma tentativa de recuperação do objeto que representa o usuário atualmente logado no sistema. Caso exista, os dados deste usuário serão utilizados para atualizar a sua flag de sessão no banco de dados, através de uma instância da classe de serviço ServicoDesativarUsuario. Por fim, a sessão é invalidada através da referência para a instância de ExternalContext, já mencionada, e a saída produzida (OUTCOME_LOGOUT) aciona uma regra de navegação do JSF que faz com que o sistema apresente a página de login para o usuário. O recurso de autenticação 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 usuários que, porventura, conheçam a estrutura básica do projeto (a qual, para este nosso exemplo, pode ser vista na Figura 2). Embora o sistema esteja configurado para apresentar, em sua inicialização, a página de autenticação, qualquer pessoa poderia tentar digitar, diretamente na barra de endereços do navegador, o caminho para qualquer página do sistema que não seja a de login. No texto que segue, veremos como é possível filtrar requisições e impedir que este
tipo de navegação seja realizado, salvo por usuários que tenham, previamente, se autenticado junto ao sistema e, consequentemente, iniciado uma sessão de navegação. Sempre que um usuário consumir uma determinada funcionalidade de um sistema em Java para a web, estará enviando pela rede dados encapsulados na forma de uma requisição construída em cima do protocolo HTTP. Para atividades de transformação de dados (cadastros, atualizações), o método comumente utilizado é o POST, enquanto para recuperação de dados apenas para fins de leitura normalmente se utilizam do método GET. A unidade de processamento de requisições web, em Java, é conhecida como servlet. O termo “let”, em computação, é normalmente utilizado para representar o diminutivo de um conceito, e servlets, neste sentido, significam pequenos servidores, unidades lógicas 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 método específico para cada método existente no protocolo HTTP (POST, GET, PUT, DELETE, OPTIONS, HEAD, TRACE). A assinatura desses métodos segue um padrão que inicia com um prefixo “do”, seguido pelo nome do método em questão. Logo, ao consultar a documentação da API (vide seção Links), o leitor verá métodos como doPost(), doPut(), doDelete(), doOptions(), dentre outros. No caso do JavaServer Faces, existe um servlet que age como um controlador central, e cuja implementação se encontra na forma de uma classe chamada javax.faces.webapp.FacesServlet. Quando uma requisição chega a este servlet, entra em um ciclo de vida definido dentro da especificação JSF, ilustrado na Figura 3. A requisição percorre esta sequência de estados e a sinalização de cada mudança é realizada na forma de um evento. O JSF possui uma família de classes para representação de eventos, tendo como unidade mais genérica 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 denominação de listener. A representação mais genérica de listeners para eventos JSF está estabelecida na interface javax.faces.event.FacesListener. Entretanto, para o caso de eventos relacionados a fases de uma requisição JSF, tal hierarquia não 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 através da interface javax.faces.event.PhaseListener que, por sua vez, herda outras especificações de outras duas interfaces utilitárias do Java, java.util.EventListener e java.io.Serializable. Antes que esta última afirmação soe um pouco estranha para o leitor, revisitemos os conceitos de orientação a objetos e, mais particularmente, como esses conceitos são implementados em Java. Eles enunciam que, embora não possamos trabalhar com herança múltipla em classes de objetos, podemos tê-la, de certo modo, aplicada a interfaces. Logo, é perfeitamente normal que uma interface como a PhaseListener estenda de especificações pré-estabelecidas em outras duas interfaces (contratos, protocolos, como preferir). Conforme já vimos anteriormente neste artigo, desejamos filtrar requisições para verificar, inicialmente, se existe uma sessão ativa para o usuário. Mais que isso, desejamos redirecionar o usuário para a página de entrada caso o usuário, ainda não “logado”, deseje acessar qualquer página interna da aplicação, via barra de endereços do navegador. Tudo começa através da implementação de um listener de fase, que pode ser visto na Listagem 4. O primeiro ponto importante a ressaltar é que o método PhaseListener.getPhaseId() é implementado para retornar sempre a instância que representa a fase RESTORE_VIEW (observe novamente a Figura 3, e note que este é o primeiro estado em que a requisição entra). Isto faz com que apenas eventos relacionados a esta fase sejam capturados, ignorando-se todos os demais eventos das fases subsequentes de uma requisição. Já no momento posterior ao da fase, o que esta classe faz é identificar, através da instância de contexto correspondente a esta requisição ( FacesContext), se a página que se deseja acessar é a de login. Caso não
seja, o próximo passo é verificar se, no objeto que representa a sessão, existe um usuário salvo (o que configura, portanto, uma sessão ativa). Caso exista, permite-se a navegação desejada, e em caso contrário, estabelece-se programaticamente um redirecionamento para a página de login através de uma instância de javax.faces.application.NavigationHandler. O método usado para isto é o handleNavigation(), em que são passados como parâmetros o contexto da requisição atual, uma ação e um outcome. Navegação em JSF trabalha tanto com outcomes quanto com ações. Através de configuração, podemos definir quais ações e resultados devem ser respeitados para que a aplicação navegue de uma página a outra. Estas configurações podem ser observadas na Listagem 5. Nesta mesma listagem, podemos ver como se configura um listener de fases para uma aplicação em Java para a web, através de um marcador