Sessão expirada sem redirecionamento - JSF 2

9 respostas
C

Olá,
Tenho uma aplicação JSF 2.0 com primefaces 2.2.1 e estou com o seguinte problema:
Utilizo o phaseListener para redirecionamento quando a sessão estiver expirada que só redireciona antes de estar logado. Após fazer o login a página não muda. Apenas perde o menu que é criado dinamicamente.

Eis os códigos da aplicação.
o web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/login.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

O faces-config.xml

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

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
    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_0.xsd">

    <application>
        <action-listener>br.com.scfweb.listener.DefaultActionListener</action-listener>
    </application>

    <lifecycle>
        <phase-listener>br.com.scfweb.listener.DefaultPhaseListener</phase-listener>
    </lifecycle>

    <navigation-rule>
        <navigation-case>
            <from-outcome>erro</from-outcome>
            <to-view-id>/views/comum/erroGeral.xhtml</to-view-id>
        </navigation-case>
    </navigation-rule>

</faces-config>

A classe implementando o PhaseListener

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.scfweb.listener;

import br.com.scfweb.utils.FacesUtil;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpSession;

public class DefaultPhaseListener implements PhaseListener {

    private static final long serialVersionUID = -1065005858605121693L;
    private static final String viewSessaoExpirada = "/faces/views/comum/erroSessaoExpirada.xhtml";

    @Override
    public void afterPhase(PhaseEvent event) {
        FacesContext context = event.getFacesContext();
        ExternalContext ext = context.getExternalContext();
        HttpSession session = (HttpSession) ext.getSession(false);
        boolean newSession = (session == null) || (session.isNew());
        boolean postback = !ext.getRequestParameterMap().isEmpty(); // Depois de logado, este objeto sempre vem false (era pra vir true)
        boolean timedout = postback && newSession;
        if (timedout) {
            FacesUtil.redirectTo(context.getExternalContext().getRequestContextPath() + viewSessaoExpirada);
        }
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        //OLD
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }
}

Então, como mostrado na classe DefaultPhaseListener, o objeto postback não recebe o valor que deveria (true).
Não estou entendendo o porque e consequentemente não consegui resolver ainda.
Agradeço a quem puder ajudar.

9 Respostas

D

qual o problema não esta redirecionando ? ee tb eu colocaria logo PhaseId.ANY_PHASE!

C

Oi dijava,

Quando expira a sessão a aplicação deveria ser redirecionada para uma página que avisa ao usuário de tal fato e disponibiliza um link para voltar a página de login. É isso que não está fazendo.

Quanto ao Phaseid, eu costumo utilizar o RESTORE_VIEW. Tenho o mesmo em outras aplicações e somente nesta da este problema.

Alguma idéia?

C

Alguém, por favor.

D

po meio estranho isso, vai debugando com calma, ja tive muitos problemas com o requestScope não é o mesmo caso que o seu, mas vai analisando o comportamento ve tudo ! JSF é muito bom mas as vezes os probleminhas sao chatinhos d resolver !

C

Bom, depois de algumas pesquisas e ajuda de colegas de trabalho, foi verificado que o trecho de código abaixo (comentado) verifica se existe alguma navegação via post. Como não existe (pois estou navegando através do menu, via get) o resultado não é o esperado e consequentemente não sou redirecionado para a página de sessão expirada.
Isso explica o porque de dar certo quando ainda não estou logado. afinal, para logar eu utilizo um post do botão de fazer login.

public void afterPhase(PhaseEvent event) {
        FacesContext context = event.getFacesContext();
        ExternalContext ext = context.getExternalContext();
        HttpSession session = (HttpSession) ext.getSession(false);
        boolean newSession = (session == null) || (session.isNew());
        //boolean postback = !ext.getRequestParameterMap().isEmpty();
        boolean timedout = postback && newSession;
        if (timedout) {
            FacesUtil.redirectTo(context.getExternalContext().getRequestContextPath() + viewSessaoExpirada);
        }
    }

Então, apesar de descobrir o problema, ainda não sei a solução para o mesmo. Resolvi, temporariamente, tirar esta condição do código para que verificado apenas se a sessão está nula. Não sei se é a solução mais acertada.

Caso alguém saiba de algo melhor, por favor me diga. Agradeço desde já.

G

Olá,

O que você quer é que NÃO faça o redirect quando um usuário esquecer um formulário aberto e submeter depois que expirar a sessão ?

C

Oi gomesrod,
O redirecionamento deve acontecer sempre que a sessão estiver expirada, mas só acontece quando existe um envio através de post (botão para ir para a página index.xhtml, por exemplo). Quando tento navegar para outra página através do menu, o redirecionamento não funciona.

Se precisar de mais detalhes é só avisar.

G

Se o que você precisa é redirecionar sempre que a sessão expirar, essa condição de postback é desnecessária e pode ser removida sem problemas!

C

Olá,

Após algumas modificações deixei o código assim:

public void afterPhase(PhaseEvent event) {
        FacesContext context = event.getFacesContext();
        ExternalContext ext = context.getExternalContext();
        HttpSession session = (HttpSession) ext.getSession(false);
        boolean newSession = (session == null) || (session.isNew());
        boolean timedout = newSession;
        if (timedout) {
            Application app = context.getApplication();
            ViewHandler viewHandler = app.getViewHandler();
            UIViewRoot view = viewHandler.createView(context, homepage);
            context.setViewRoot(view);
            context.renderResponse();
            try {
                viewHandler.renderView(context, view);
                context.responseComplete();
            } catch (Throwable t) {
                throw new FacesException("Session timed out", t);
            }
        }
    }

Parecia estar tudo resolvido, mas não estava.
Acontece que agora tenho a exceção javax.faces.application.ViewExpiredException. Vejam o trecho de código do erro gerado:

javax.faces.application.ViewExpiredException: viewId:/login.xhtml - A exibição de /login.xhtml não pode ser restaurada.
	at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:195)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
	at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:107)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
        .
        .
        .

Bom, por enquanto e infelizmente, resolvi mudar meu session-timeout para -1. Dessa forma mantenho a sessão aberta sempre e faço isso apenas porque a aplicação ainda é pequena e tal configuração não terá impacto negativo no desempenho da mesma.
Continuarei com este post em aberto enquanto busco uma solução definitiva ou quem sabe, até que algum colega consiga resolver e compartilhe aqui.

Abraço a todos!

Criado 19 de abril de 2011
Ultima resposta 25 de abr. de 2011
Respostas 9
Participantes 3