Nova versão do JSF 2 não dispara o FORWARD na navegação das paginas XHTML [RESOLVIDO]

Ola galera

Na hora que vc faz um regra de navegação do faces-config, a especificação diz que o JSF navega via FORWARD de uma página para outra.
O teste disso pode fazer um filtro simples que intercepta tudo, usando todos os dispachers possiveis:

	<filter>
		<filter-name>FiltroTeste</filter-name>
		<filter-class>controle.FiltrarRestricoes</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>FiltroTeste</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>INCLUDE</dispatcher>
		<dispatcher>ERROR</dispatcher>
	</filter-mapping>

Em qualquer versão do JSF 1.x, vc pode navegar de uma pagina para outra que o o filtro é disparado.
Segundo informações da especificação do JSF :

Estou a 1 semana ajudando um camarada aqui no forum a implementar um filtro de autenticação para aplicações JSF 2 e pude comprovar que na versão 2 do JSF, as navegações não estão invocando o FORWARD e assim o o filtro não é disparado.
Montei um ambiente de teste com MOJARRA e MyFaces para JSF 2 e realmente não esta invocando!! Montei um ambiente teste com Mojara 1.2 e o FORWARD é chamado e filtro funciona!
Não achei nada a web sobre isso, crie um post no JBOSS Forum e no JavaRanch até agora sem respostas…
Eu sei que o ciclo de vida da execução JSF foi alterada para facelets nativamente com as paginas XHTML.
Fiz o mesmo teste JSF 2 usando paginas JSP…dai funcionou!!!
A questão por que isso não funciona com XHTML? Como ficam os projetos JSF 2 com frameworks baseados em filtros com Spring Security, JPro-Guard e até alguns JAAS? uma vez que apresentaram brechas de segurança por causa desse motivo.
Alguem tem alguma informação?

Bom gente esta ai a resposta…para container isso, os projetos antigos deverão colocar <REDIRECT/> nas navegações JSF 2 para usar regras de autenticação baseado em filtros.
O post fica aberto para qualquer outra pessoa encontrar outras soluções para isso…favor postar aqui.
T+

A solução que me parece ideal é usar autenticação baseada em PHASE-LISTENER -
http://rodrigolazoti.com.br/2008/09/01/filtrando-usuarios-logados-em-jsf-com-phaselistener/
http://ciromacedo.blogspot.com/2008/06/controle-de-acesso-no-jsf.html


Mas eu não testei…valido fazer um teste ai…

pois é FernandoFranzini, depois de pesquisar bastante e ler vários textos, os melhores q eu achei foram
http://java.dzone.com/articles/fluent-navigation-jsf-2
http://java.dzone.com/articles/fluent-navigation-jsf-2?page=0,1
http://blog.gilliard.eti.br/2009/05/implicit-navigation-jsf-2/
e infelizmente ainda não achei nenhuma maneira de recuperar o nomeDaPágina.xhtml requisitada e processada pelo facelets, como já faz vários dias q estou pesquisando sobre isso e ainda não encontrei nd, estou pensando em fazer o seguinte, e dar continuidade ao projeto…
não tava afim de ficar declarando regras de navegação no faces-config.xml, esse é um dos motivos que fizeram eu nao trabalhar com web por um bom tempo, eu detesto arquivos de configuração e quanto menos precisar mecher neles, mais feliz eu fico, levando em consideração a parada de navegação implicita, que se o outcome informado na action do commandlink não for encontrado no faces-config, a navegação implicita assume que este é o nome da pagina a ser carregada, nas páginas que vou precisar usar o filter para autorização de usuario, vou concatenar o parametro ?faces-redirect=true na action do commandlink ± assim<h:form> <h:commandLink action="pagina1" value="pagina1" /> <br/> <h:commandLink action="pagina2?faces-redirect=true" value="pagina2" /> <br/> Hello from Facelets </h:form>isso ficou legal, nao preciso declarar regras de navegação e o parametro faces-redirect faz o trabalho da tag necessária na regra de navegação, por enquanto isso resolveu o meu problema de login no JSF 2.0, pra quem não viu o problema, tah aqui http://www.guj.com.br/java/247084-login-aplicacao-jsf dai é só usar a tag como o fernandofranzini sugeriu no faces-config.xml ou se preferir o parametro faces-redirect

FernandoFranzini, obrigado pelo empenho ai em descobrir alguma solução, vlw pela ajuda, mas como preciso colocar o site no ar até fim do mes, vou acelerar aqui, mas pretendo continuar tentando descobrir uma forma de recuperar a URL requisitada e processada pelo facelets assim que folgar um pouco aqui.

vlw t+

Achei outra forma com phase listener…
Remova o filter e use:

[code]
public class AuthorizationListener2 implements PhaseListener {

public void afterPhase(PhaseEvent event) {
	FacesContext facesContext = event.getFacesContext();
	String currentPage = facesContext.getViewRoot().getViewId();
	System.out.println("PHASE - " + currentPage);
}

public void beforePhase(PhaseEvent event) {
}

public PhaseId getPhaseId() {
	return PhaseId.RENDER_RESPONSE;
}

}[/code]
Dai vc declara no faces config

	&lt;lifecycle&gt;
		&lt;phase-listener&gt;controle.AuthorizationListener2&lt;/phase-listener&gt;
	&lt;/lifecycle&gt;

E faça o mesmo teste…
Dai dentro do face vc verifica a pagina que o JSF esta renderizando e veja se o usuario tem a PERMISSÃO para acessa-la…caso contrario, rederecione para a pagina de login…veja outros exemplos nos links que eu passe de como fazer isso.
T+

o phase listener apresenta o mesmo problema do filter, URL atrasada e usando a mesma solução acima, ou ?faces-redirect=true passa a funcionar

Pelo que eu testei o phase listener apresenta o mesmo problema se a fase interceptadas for RESTORE_VIEW…
Mas como vc pode ver eu estou falando da RENDER_RESPONSE que ja tem o novo endereço (nova visão)
Acho que fecho…

puts cara, finalmente deu certo, usando phase listener, veja só, eu tentei várias vezes com phase listener, mas olhando a sua classe e a minha, tinha algo diferente, alterei na minha e funcionou certinho com phase listener, inclusive sem utilizar ou ?faces-redirect=true. Vamos lá então, pra encerrar a história, kkkk ficou assim:

suponha que temos 4 paginas, index.xhtml, pagina1.xhtml, pagina2.xhtml e acessoNegado.xhtml

faces-config.xml[code]<?xml version='1.0' encoding='UTF-8'?>

<lifecycle>
    <phase-listener>controle.AuthorizationListener</phase-listener>
</lifecycle>

[/code]index.xhtml[code]<?xml version='1.0' encoding='UTF-8' ?>

Facelet Title

Hello from Facelets [/code]AuthorizationListener (se a pagina requisitada for pagina2.xhtml, redireciona para acessoNegado.xhtml)[code]public class AuthorizationListener implements PhaseListener {
@Override
public void afterPhase(PhaseEvent event) {
    FacesContext facesContext = event.getFacesContext();
    String currentPage = facesContext.getViewRoot().getViewId();

    System.out.println("currentPage: " + currentPage);
    if (currentPage.contains("pagina2.xhtml")) {
        NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
        nh.handleNavigation(facesContext, null, "acessoNegado.xhtml");
    }
}

@Override
public void beforePhase(PhaseEvent event) {
}

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

}[/code]FernandoFranzini, repare no método getPhaseId() eu usava a linha comentada, mudando para RENDER_RESPONSE a URL não fica mais atrasada na lógica de autorização do método afterPhase(…). Bom, e assim tah legal, é só acrescentar a logica de validação do usuario e pronto…

Isso mesmo cleiton…acho que chegamos numa solução pratica, centralizada e reutilizavel.
Eu tb vou isso nos proximos projeto JSF 2…os antigos 1.2 ja estam rodando a muito tempo com filter funcionando ok com foward.
Uma coisa que eu vi no seu codigo q ta errado:

Vc não deve por o nome da pagina e sim o nome do alias da pagina declarado no faces config…quando vc mudar a pagina o alias fica o mesmo.

No resto parece ok.

então fernando, eu tava pensando em utilizar esse novo recurso de navegação implicita da versão 2.0, nem tava querendo declarar navegações no faces-config.xml, até se vc ver ali no meu faces-config de teste, nem declarei nd, masssss, como vc tem mais experiencia… o que vc me diz sobre esse novo recurso e sobre não declarar regras de navegação no faces-config ?

Pelo pouco que eu sei dos novos recursos do JSF 2 é para evitar ter que escrever no XML…
Acredito que não tenha problema em usar…fica mais complicadinho fazer o codigo, mas vc fica isento de xml.

Então cleiton

Acabei de achar um problema nessa segurança kkkk…calma…relaxa… :lol:

*) Phase listerner de RENDER_RESPONSE é executado somente depois da ação do bean…#{bean.acao}!!!
Se caso o usuario não estiver logado ou expirar a sessão dele…se ele clickar no botão da GUI o sistema vai executar a ação do botão e depois disso de sera enviado a pagina de login. E nós não queremos isso né…

Por isso, para precisar usar 2 listener…

  1. Listener de RESTORE_VIEW que não deixa o usuário executar a ação se não estiver logado
  2. Outro listener de RENDER_RESPONSE que não deixa o usuário ver a pagina se ele não possuir permissão.
    Dai vc pode centralizar essas regras em objetos do seu sistema de SEGURANÇA e invoca-los nestes listeners…
    Veja que na solução anterior ao filtro…estas 2 validações estavam juntos…

legal, mais um problema que provavelmente eu ia identificar na hora de implementar a logica de validação do usuario, hahahha e vc ja solucionou…

então blz, vou implementar os dois phaselisteners aqui e ver no q da

Estava pensando aqui com meu botões…seria melhor se agente mantivesse o filtro como esta e acrescentássemos uma validação similar ao do filtro no fase RENDER_RESPONSE para validar somente os casos de navegações internas do facelets…Por que pensa comigo:
Por experiência própria eu não consigo ver um cenário onde barrar via forward seja necessário. Pois normalmente você barra uma URI (isto é, uma requisição GET na URI ou redirect) ou um evento de uma página (origem) previamente renderizada pro usuário…seria casos excepcionais…tipos casos em que vc esquecesse de retirar um botão da GUI no qual o usuário não teria permissão de invocar…(conhecido como permissão por função)
Esse código de validação do fase RENDER_RESPONSE serie apenas de segurança extra mesmo…algo assim questionável.

então eu tbem estava analisando aqui, e mesmo com duas que implementam phase listener, uma pra RESTORE_VIEW e outra pra RENDER_RESPONSE, na RENDER_VIEW por exemplo, eu nao posso redirecionar o usuario direto pra pagina de login simplesmente por ele não estar logado, levando em consideração que existem páginas que não é necessário estar logado para navegar, então eu continuaria precisando saber o nome da pagina requisitada, e isso só se consegue na RENDER_RESPONSE

no meu caso, o certo é verificar usuario e permissão juntos, senão nao da… pelo menos eu nao consegui visualizar isso até agora…

[quote=cleiton herrmann]então eu tbem estava analisando aqui, e mesmo com duas que implementam phase listener, uma pra RESTORE_VIEW e outra pra RENDER_RESPONSE, na RENDER_VIEW por exemplo, eu nao posso redirecionar o usuario direto pra pagina de login simplesmente por ele não estar logado, levando em consideração que existem páginas que não é necessário estar logado para navegar, então eu continuaria precisando saber o nome da pagina requisitada, e isso só se consegue na RENDER_RESPONSE
no meu caso, o certo é verificar usuario e permissão juntos, senão nao da… pelo menos eu nao consegui visualizar isso até agora…[/quote]

Mas um motivo para usar o filtro…

  1. usa o filtro mesmo.
  2. reutiliza a validação no RENDER_RESPONSE

Pensa…
1)Quando chegar uma URL solicitada via navegador o filtro vai aplicar a validação antes de chegar no facesServlet e vai mandar para o login sem passar pelo JSF. Então não executa o fase.
2) Quando vc navegador da pagina 1 para pagina 2, o filtro não vai ser executado e fase RENDER_RESPONSE sera executado garantido as validações.
3) Quando chegar uma URL solicitada via navegador o filtro vai aplicar a validação antes de chegar no facesServlet, se a permisão bater…o fase vai refazer a validação e liberar o acesso. Unico ponto negativo que vai valida 2 vezes para esses casos…

Na verdade tem fazer testes em um estudo de caso simples para pegar todas os casos…

Cleiton
O que vc acha que fica melhor?
1) Usa redirect e fazer só o filtro?
ou
2) Executar as validação 2 vezes no filtro e na fase RENDER_RESPONSE? (que tem a probabilidade de ser 90% das paginas…o cara autenticado com as permissões ok)

Pros e contras?

Acho que resolvi…

  1. Chama a regra de autenticação no filtro
    O filtro fica melhor que pode haver no seus sistema outros endereços para coisas não faces, como arquivos, imagens, relatório etc que tb estão dentro da validação de URL…se agente usar o fhase de Restore VIEW ele naõ vai pegar esses outros endereços…dai não vai dar…

  2. Chama a regra de autenticação no phase de RENDER_RESPONSE:
    Mas para não perder tempo/recursos da segunda validação desnecessária é só verificar a URI do fhase é igual a URI do filtro…se forem iguais não precisa aplicar a segunda validação, podemos pular via if…se forem diferentes…dai vc valida toda a regra de segurança de novo…pq pensa bem…só vai ser diferente quando vc navegar da paginaA para a paginaB.
    Para comparar as duas…é só colocar o URI do filtro numa variável ThreadLocal e acessar dentro do fhase listener…

Dai fica lindooooo e não perdemos tempo validando as regras de autenticação duplicadamente se caso ele chamou uma ação e não navegou…

[quote]Cleiton
O que vc acha que fica melhor?

  1. Usa redirect e fazer só o filtro?
    ou
  2. Executar as validação 2 vezes no filtro e na fase RENDER_RESPONSE? (que tem a probabilidade de ser 90% das paginas…o cara autenticado com as permissões ok)

Pros e contras?[/quote]
pois é fernando, o jeito mais simples na minha opinião é usar filter e concatenar o parametro ?faces-redirect=true na action do commandlink, nas paginas q eu quero validar e autorizar acesso

EDIT
Então cara, gastei mais meia horinha agora aqui, acho q nao captei a idéia de usar o filtro e o phase em conjunto, não entendi o pq…
eu implementei aqui usando só o filtro, fiz desse jeito q falei, nao declarei regra nenhuma de navegação no faces-config, e nos commandlink das paginas que requerem autorização eu concatenei o ?faces-redirect=true, e dai na classe filter, eu verifico atraves de if, se a pagina corrente é uma das que precisam de usuario logado, e se sim, eu verifico se tem um usuario logado, se sim, continua normal, senão volto pra index, que é onde eu coloquei meus campos de usuario e senha… fico legal assim eu achei, nao achei necessário usar o phase tbem, na verdade nao entendi sua ideia de usar os dois em conjunto

Então cleiton eu acho que vc fez sua decisão e realmente assim tb funciona e fica bom.
Eu particularmente não escolheria esta opção pois esse esquema de redirect tanto na url como no xml afeta diretamente a performance da aplicação, uma vez que o container vai mandar um HTTP 301 e o navegador vai ter que fazer sempre + 3 viagens HTTP. Se vc ja diagnosticou que isso não sera um gargalo, então ok.
Estarei documentando tudo isso no meu blog colocando os pros e contras das 3 opções que levantamos…
Existe uma terceira opção que ja bolei aqui q tb ta certo usando somente filter e anotações de segurança JEE 6 @Role.