Dúvida sobre login com JSF

06/10/2010

Estou desenvolvendo meu trabalho de conclusão de curso e estou tendo dificuldades em implementar a funcionalidade de login com JSF. Além do login tenho que gerar sessões pois todas as páginas devem ser acessadas por pessoas autorizadas.

Vi a vídeo aula número 15 do curso desenvolvendo uma aplicação com JPA ministrada pelo Dyego Carmo e fiz algumas adaptações para meu problema, mais ao executar a aplicação aparece a seguinte mensagem de erro:

"java.lang.IllegalArgumentException: org.hibernate.QueryException: JPA-style positional param was not an integral ordinal "

Estrutura criada. Gerei uma unidade de persistencia chamada SISMOTPU2. Foi gerada um loginFace e uma classe Chamada TesteLoginDAO que implementa o metodo public boolean isUsernameAndPasswordExists(String username, String password){ }

TesteLoginDAO: http://paste-it.net/public/d32e7cf/
LoginFace: http://paste-it.net/public/p1b57aa/

Dentro deste metodo gerei uma instancia do EntityManagerFactory e do entityManager, a query de consulta foi personalizada e realiza uma pesquisa junto a tabela funcionario, abaixo destaco a query

em.createQuery("select nomeFuncionario from Funcionario f where f.id = ?1"+
"and f.senha = ?2");

Ao executar o debug da aplicação o único valor que consigo visualizar não nulo e o campo password o
campo username sempre vem nulo

Alem das classes foi gerada a tela de login

areaLogin.jsp : http://paste-it.net/public/n44acea/

no faces config configurei para caso tenha sucesso no login ele direcionar para a seguinte pagina
 principal.jsp na raiz do diretorio web.

Estou utilizando o hibernate em minha aplicação, caso eu faça esta funcionalidade com JPA, estarei
 rompendo algum padrão de projeto?

O que devo fazer para solucionar este problema?

Segue abaixo link para acessar o codigo do projeto

src: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/classes/src_4.rar
web: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/web/web_4.rar
imagens: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/telas/telaLogin.rar

Desde já agradeço.

Marcos Sousa

Marcos Sousa

Curtidas 0

Respostas

Marcos Sousa

Marcos Sousa

06/10/2010

Esqueci de informar o back up do banco de dados. Segue abaixo link do back up do banco

http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/script_banco/db_sismot 20101006 0015.rar

Os dados para efetuar login são
Matricula : 1
Senha: 123

Há somente um funcionário cadastrado.


GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Olá, o problema está no índice dos parâmetros passados na sua classe TesteLoginDAO. No caso do JPA, os parâmetros são iniciados com índice 0. Sendo assim, ao invés de passar os parametros nas ordens 1 e 2, tente passá-los na ordem 0 e 1.
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Boa tarde Henrique. Fiz a alteração que voce me sugeriu e realizei um debug sobre a aplicação mais ele continua retornando a seguinte mensagem de erro junto a tela de login.

"java.lang.IllegalArgumentException: org.hibernate.QueryException: JPA-style positional param was not an integral ordinal "

na classe LoginFace coloquei break points nas seguintes linhas.

 public void validateLogin(FacesContext context, UIComponent component, Object value) throws ValidatorException {

        System.out.print("The username "+username + " password "+password +" value to string "+ value.toString());
        // breakpoint System.out.print("The usernameInput "+usernameInput.getLocalValue().toString() + " passwordInput "+passwordInput.getSubmittedValue() +" value to string "+ value.toString());
      //BreakPoint  boolean exists = new TesteLoginDAO().isUsernameAndPasswordExists(username, value.toString());
        if(!exists){
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "MATRICULA/SENHA INVALIDOS", "MATRICULA/SENHA INVALIDOS");
            throw new ValidatorException(message);
        }
    }
}

Ao visualizar os breakpoints deste metodo o username fica null e o unico valor que e passado e o value.toString() que carrega a senha.
O usernameInput e o passwordInput não consegui visualizar os valores para eles.

acesso ao loginFace: http://paste-it.net/public/rc11940/

no método isUsernameAndPasswordExists fiz as alterações que voce me solicitou que era de inicializar os parametros a partir do 0.

Envio a classe com para analise: http://paste-it.net/public/x292ff8/

O que devo fazer para corrigir está falha? Desde já agradeço.


GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Marcos,


Testando seu código no meu editor, observei um detalhe interessante.
Primeiro, veja o seu código original:

public boolean isUsernameAndPasswordExists(String username, String password){ boolean exists = false; EntityManagerFactory emf = Persistence.createEntityManagerFactory("sismotPU2"); EntityManager em = emf.createEntityManager(); //breakpoint em.getTransaction().begin(); //breakpoint //Alterei a query para ?0 e ?1 inseri um break point nesta linha Query qr = em.createQuery("select nomeFuncionario from Funcionario f where f.id = ?0"+ "and f.senha = ?1"); qr.setParameter(0, username); //breakpoint linha não executada qr.setParameter(1, password); try { exists = qr.getSingleResult() != null; //breakpoint linha não executada } catch (Exception e) { exists = false; } em.getTransaction().commit(); em.close(); return exists; }

agora, repare neste trecho:

Query qr = em.createQuery("select nomeFuncionario from Funcionario f where f.id = ?0"+ "and f.senha = ?1");

O que ocorre: o problema está na concatenação da String.
O resultado desta concatenação será

"select nomeFuncionario from Funcionario f where f.id = ?0and f.senha = ?1"

Coloque um espaço antes da primeira concatenação que o problema deverá ser resolvido.

GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Henrique adicionei o espaço na linha da query conforme sua orientação.

Query qr = em.createQuery("select nomeFuncionario from Funcionario f where f.id = ?0"+
                 " and f.senha = ?1");

Inseri breakpoints nas linhas e executei minha aplicação em modo debug, mais quando clico no botão Login ele não inicia o debug e me retorna o seguinte erro "
javax.servlet.ServletException: Target Unreachable, 'funcionario' returned null"

O que devo fazer para corrigir está falha.

Segue abaixo link para acesso aos codigos da aplicação.

source: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/classes/srcLoginrar.rar
paginas web: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/web/webLogin.rar

links para acesso ao
arealogin.jsp: http://paste-it.net/public/ee14d22/
FuncionarioBean: http://paste-it.net/public/lb51595/
TesteLoginDAO: http://paste-it.net/public/t3ec2f3/
persistence.xml: http://paste-it.net/public/x8f7c29/

desde já agradeço.
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Marcos, tenho algumas observações para o seu código. Com relação ao managed bean FuncionarioBean. Não é preciso fazer o binding entre os campos do seu formulário e os componentes diretos da classe. Você poderia fazer algo assim: por exemplo, em que apenas aponte para a propriedade login desta classe. Com relação ao método chamado no bean, o doLogin, é estranho você obter este erro, porque seu método não tem como retornar um valor nulo, visto que, pelo que pude ler no seu texto, ele apenas adiciona a variável logged na sessão com o valor true. O código que você está executando está realmente no formulário de login que você me passou neste link? http://paste-it.net/public/ee14d22/
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Boa noite Henrique. Descobri o que estava gerando este problema no formulario areaLogin.jsp. Dentro de um <h:form> eu tinha dois commandButton
<h:commandButton value="Logar" action="#{funcionarioBean.efetuarLogin}" /><h:commandButton value="Home" action="#{funcionarioBean.home}" />Retirei os binding's do formularios e criei uma classe chamada FuncionarioDAOImpl onde tenho o metodo consultarLoginESenha
http://paste-it.net/public/b770744/

ao chamar a action do botão logar ele executa o metodo efetuarLogin de minha classe FuncionarioBean
http://paste-it.net/public/ceea679/AreaLogin.jsp: http://paste-it.net/public/p643a6c/

Agora ele esta fazendo o login, minha dúvida agora e a seguinte testei o metodo validateLogin da classe FuncionarioBean mais ele não funcionou como deveria me retornando a mensagem de usuario/senha invalidos. Com este erro ele me retorna um nullPointerException

como corrigo este problema?

Segue codigo fonte para analise
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Havia esquecido de disponibilizar os codigos fontes do projeto. Seguem abaixo link para acesso

Web: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/web/webLogin2.rar
src: http://video.devmedia.com.br/discovirtual/210904/projeto SISMOT/classes/srcLogin2.rar

Desde já agradeço.
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Marcos, com base na sua classe FuncionarioBean, há duas possibilidades para o erro NullPointerException.
No caso, tratam-se dos atributos testeDAO e funcionario. Observei que no seu código fonte, você declara estas variáveis já as definindo como novas instâncias de suas respectivas classes, no entanto, há também dois métodos do tipo set que as definem. Verifique os seguintes pontos:
Você está passando o valor null para algum destes setters?
Dica para evitar um NullPointerException: nos seus métodos, sempre verifique os valores das variáveis antes de acessá-las. Assim você pode detectar a presença de um atributo nulo antes que uma excessão seja disparada.
Outro ponto a ser verificado. Será que o erro não está ocorrendo dentro de TesteLoginDAO?
Dica: verifique o seu teste unitário ou o processo que leva à execução de suas classes. Muitas vezes podemos estar criando classes que, acidentalmente, só funcionem após a execução de um número pré-determinado de passos e que, em outro contexto, por não estarem prontas, gerem estes chatos erros do tipo NullPointerException.
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Boa tarde Henrique, o problema sobre o nullPointerException foi gerado por este fator que voce me falou. Agora meu problema e garantir acesso as páginas da aplicação após a realização do login.
Tenho dentro da página welcomeJSF.jsp uma chamada a página de login.
<jsp:forward page="index.jsp"></jsp:forward>

Apos realizar o login nesta página sou direcionado para a pagina localizada em sismot/faces/principal.jsp, quero criar uma sessão é impedir o acesso direto a está página as pessoas que não efetuarem o login na aplicação. voce pode me demonstrar um exemplo para implementar isso?



Desde já agradeço.

Segue abaixo link das paginas:

index: http://paste-it.net/public/n1c1b42/
principal: http://paste-it.net/public/i0a82c0/

GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Oi Marcos,
Neste caso, você precisa implementar um filtro. A plataforma Java EE nos fornece este recurso, que funciona da seguinte forma: você cria uma classe que implemente a interface javax.servlet.Filter e em seguida as configurações desta classe no arquivo web.xml da sua aplicação.
O filtro intercepta todas as chamadas ao seu servidor e em seguida faz os redirecionamentos caso necessário. Ele inclusive tem acesso à sessão do usuário. No seu caso, vai ser simples: caso a variável logado não esteja presente, ele fará o redirecionamento para alguma outra página de sua escolha.

O melhor exemplo que conheço do uso desta técnica é este tutorial presente no GUJ: http://www.guj.com.br/article.show.logic?id=11

GOSTEI 0
Devmedia

Devmedia

06/10/2010

Marcos, a resposta do consultor tirou suas duvidas? Podemos encerrar o chamado?
GOSTEI 0
Devmedia

Devmedia

06/10/2010

Marcos, a resposta do consultor tirou suas duvidas? Podemos encerrar o chamado?
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Peço que aguarde até hoje para que eu possa implementar o exemplo que o Henrique me passou. Não tive tempo para estudar o tutorial.

Desde já agradeço.
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Criei um pacote em minha aplicação chamado filtro e dentro dele implementei o FilterTime conforme exemplo do guj: http://paste-it.net/public/bc4ddb0/

Em seguida configuerei meu Xml da seguinte forma:

 <filter>
        <filter-name>timerFilter</filter-name>
        <filter-class>filtro.TimerFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>timerFilter</filter-name>
        <url-pattern>/faces/*</url-pattern>
    </filter-mapping>

E o navegador me retornou um erro 503. Alterei a configuração do filtro para a seguinte:

<filter>
        <filter-name>timerFilter</filter-name>
        <filter-class>filtro.TimerFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>timerFilter</filter-name>
        <url-pattern>/sismot/*</url-pattern>
    </filter-mapping>

E o erro foi o mesmo. O que devo fazer para resolver este problema. Estes metodos devem ser implementados no meu Bean responsavel por efetuar o login que e o FuncionarioBean?

xml: http://paste-it.net/public/v7e5b3b/
erroFiltro: http://paste-it.net/public/nea777f/
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Marcos,

Você só deve invocar chain.doFilter no final do seu método.
Caso o erro continue, por favor, poste aqui o stacktrace ok?
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Fiz a alteração que voce me sugeriu e ele retornou um erro 503. Segue abaixo o erro do servidor stackTrace

http://paste-it.net/public/rd36640/E a classe timerFilter com a correção solicitada:
http://paste-it.net/public/re607b7/
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Marcos, o seu stacktrace não aponta erro do filtro, mas sim do deploy da aplicação.

java.lang.Exception: java.lang.Exception: WEB0113: Virtual server [server] already has a web module loaded at [/sismot]; therefore web module [sismot] cannot be loaded at this context path on this virtual server.


Faça o undeploy da aplicação de mesmo nome e depois o deploy novamente que deve resolver este problema.
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Henrique eu fiz o undeploy da aplicação e não resolvi o problema. Além do undeploy fiz algumas alterações no web.xml na propriedade do <filter> para tentar solucionar o problema. Segue abaixo o que foi realizado.


<filter>         <filter-name>timerFilter</filter-name>         <filter-class>filtro.TimerFilter</filter-class>     </filter>     <filter-mapping>         <filter-name>timerFilter</filter-name>         <url-pattern>/sismot/faces</url-pattern>     </filter-mapping> Ele me gerou o seguinte StrackTrace:  erroFiltro 1: http://paste-it.net/public/s7c1636/ Mensagem principal: SEVERE: WebModule[/sismot]PWC1270: Exception starting filter timerFilter java.lang.ClassCastException: filtro.TimerFilter cannot be cast to javax.servlet.Filter Reconfigurei o filter para:   <filter>         <filter-name>timerFilter</filter-name>         <filter-class>filtro.TimerFilter</filter-class>     </filter>     <filter-mapping>         <filter-name>timerFilter</filter-name>         <url-pattern>/sismot/faces/*</url-pattern>     </filter-mapping> Ele gerou o stackTrace : erroFiltro2: http://paste-it.net/public/z6277b5/   SEVERE: WebModule[/sismot]PWC1270: Exception starting filter timerFilter java.lang.ClassCastException: filtro.TimerFilter cannot be cast to javax.servlet.Filter Fiz uma última alteração no filter <filter>         <filter-name>timerFilter</filter-name>         <filter-class>filtro.TimerFilter</filter-class>     </filter>     <filter-mapping>         <filter-name>timerFilter</filter-name>         <url-pattern>/sismot/</url-pattern>     </filter-mapping> Ele gerou o stackTrace: erroFiltro 3: http://paste-it.net/public/g2f9c33/ O que esta acontecendo na configuração deste filtro para ele gerar isso? Desde já agradeço.
GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

O problema está no código fonte da sua classe TimerFilter.
Ela está na realidade implementando a interface Filter do pacote java.util.logging.Filter, e não javax.servlet.Filter. Então, na hora de fazer a instanciação, o container está encontrando uma instância diferente da que estava esperando.
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Boa noite Henrique. Fiz a alteração sugerida por voce e o codigo executou, mais eu não sei o que fazer com esta classe TimerFilter criada.

O managedBean responsável por verificar o login e senha para acesso a minha aplicação e o funcionarioBean, cujo metodo utilizado e o efetuarLogin. Em TimerFilter tenho o método doFilter que creio eu seja responsável por bloquear o acesso aos sistema digitando a url de acesso como no exemplo:

http://localhost:8080/sismot/faces/welcomeJSF.jsp

Como devo utilizar este metodo criado na classe TimerFilter para fazer isso? Após feita esta correção como faço para inserir junto a pagina principal da aplicação a mensagem

Seja bem vindo      <nome usuario>            logout?


Desde já agradeço.


GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Oi Marcos,

procurando por exemplos de aplicação de filtros com JSF, topei com este tópico no antigo fórum da Sun que você vai gostar. É exatamente o que você está procurando.

http://forums.sun.com/thread.jspa?forumID=881&threadID=5050520


GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Henrique não consegui entender. Quero saber como utilizo ele, devo inserir estes metodos do Filter em meu funcionarioBean e atraves do metodo efetuarLogin chamar a ação dele?

após isto devo fazer alguma configuração a mais no web.xml

GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Oi Marcos,

na realidade, você jamais usará o filtro diretamente. Quem o usa é o container da aplicação. A cada requisição, é verificado se a URL em questão base com algum padrão de URL presente no arquivo de configuração web.xml referente a um filtro.

Existindo o filtro, este é em seguida instanciado e executado pelo container.
GOSTEI 0
Devmedia

Devmedia

06/10/2010

Marcos,
a sua dúvida foi resolvida? Podemos encerrar o chamado?
GOSTEI 0
Devmedia

Devmedia

06/10/2010

Marcos, por falta de retorno estamos encerrando o chamado. Caso tenha dúvidas sobre o assunto, volte a postar aqui e o consultor voltará a lhe atender.
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Peço desculpas pela demora em meu retorno. Não consegui fazer o que foi solicitado com as dicas informadas pelo Henrique, resolvi o problema da seguinte forma.

Em meu arquivo faces config.xml inseri as seguintes linhas:

<lifecycle>
        <phase-listener>
br.com.sismot.filter.AutorizationListener
        </phase-listener>
</lifecycle>

Após isto criei a seguinte classe AutorizationListener: http://paste-it.net/public/qd05971/

Nesta classe tenho o seguinte método afterPhase onde inserir a seguinte estrutura

//neste if confiro se o funcionario esta logado no sistema, caso não esteja redireciono para a pagina de login que e a index.jsp
if(!isLoginPage && funcionario == null){
           NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            try {
                facesContext.getExternalContext().redirect("/sismot/faces/index.jsp");
            } catch (IOException ex) {
                Logger.getLogger(AutorizationListener.class.getName()).log(Level.SEVERE, null, ex);
            }
          
       } else{

       }


GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

Ok Marcos, o problema foi resolvido então?
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

O problema foi solucionado, só gostaria de saber as diferenças entre as duas soluções. A primeira que voce me sugeriu e a ultima que implementei e tive exito.

Por que a primeira não funcionou como o esperado.

GOSTEI 0
Henrique Weissmann

Henrique Weissmann

06/10/2010

A diferença é que na primeira solução, implementando um filtro, vocè está trabalhando diretamente com as URLs que o servidor precisa processar, ao passo que na segunda, está criando um listener para as fases de processamento do JSF.
Sendo assim, vocè está na realidade, interrompendo o processo que gera a renderização das páginas do JSF e, assim, redirecionando o usuário para outra página caso não esteja logado. Muito interessante.
É inclusive uma solução bastante interessante esta que vocè bolou, nunca vi nenhuma similar (até então, em 100% das vezes, vi o controle de acesso em Java sendo feito por filtros). Parabéns pela originalidade!
GOSTEI 0
Marcos Sousa

Marcos Sousa

06/10/2010

Muito obrigado pelos elogios.
Desde já agradeço pelos esclarecimentos.

GOSTEI 0
POSTAR