Criando uma Página de Login Completa com JSF e Primefaces

Escrito em 04/09/2017 11:29 por Benigno

Uma dúvida muito frequente quando se esta trabalhando na web com uma nova tecnologia é como implementar uma página de login que controle o acesso dos usuários. Neste tutorial irei demonstrar uma forma fácil e prática de desenvolver tal artefato de uma forma que consiga reaproveitar o código em todos os seus projetos.

Quando iniciamos nossos projetos em Java com JSF nunca encontramos um exemplo fácil e que demonstrasse com clareza como controlar o acesso dos usuários, por isso, fizemos este tutorial. Provavelmente este será o primeiro post referente à controle de acesso, posteriormente iremos incluir outros posts demonstrando como controlar permissões e como realizar os cadastros de usuários e perfis de usuários. Hands On!

Neste exemplo utilizaremos as seguintes tecnologias:

NetBeans 8.1 com GlassFish 4
Mojarra 2.2.0
Primefaces 6.1

Para começar, vamos criar nosso projeto, aqui chamei ele de loginjsf.

Categoria : Maven
Projeto: Aplicação Web

 

Preencha os dados do projeto como: Nome do Projeto, Localização, pacote, etc.

 

 

Selecione o Glassfish 4 e a versão do Java EE 7 Web

 

 

Para este projeto utilizaremos alguns pacotes para organizar melhor as classes que iremos trabalhar. A organização dos pacotes ficou distribuída conforme a imagem abaixo:

 

 

Um pacote para os DAO’s, outro para os MB, outro para os modelos, um para o PhaseListener e um pacote utils. Vamos iniciar configurando o modelo Usuario. No pacote br.com.benignosales.loginjsf.modelos crie a classe Usuario.java e nela coloque o seguinte código:

Usuario.java

 

public class Usuario implements Serializable {

    private Long id;
    private String nome;
    private String login;
    private String senha;
    private static final Logger LOG = Logger.getLogger(Usuario.class.getName());
    

    public Usuario() {
    }

    public Usuario(String nome, String login, String senha) {
        this.nome = nome;
        this.login = login;
        this.senha = new LoginUtil().MD5(senha);
    }    

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getSenha() {
        return senha;
    }

    public void setSenha(String senha) {        
        this.senha = new LoginUtil().MD5(senha);
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 83 * hash + Objects.hashCode(this.login);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Usuario other = (Usuario) obj;
        if (!Objects.equals(this.login, other.login)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return nome;
    }

 

Agora vamos criar a nossa classe de utilitários, LoginUtils.java, que neste primeiro post conterá apenas um método:

LoginUtils.java

 

public class LoginUtil {

    private static final Logger LOG = Logger.getLogger(LoginUtil.class.getName());       

    public String MD5(String senha) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(senha.getBytes());
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < array.length; ++i) {
                sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
        return null;
    }

}

Vamos agora à codificação do nosso bean gerenciado. Neste projeto utilizamos CDI para realizar a injeção do DAO no Managed Bean.


@Named
@SessionScoped
public class LoginBean implements Serializable {

    private Usuario usuarioLogado;
    private String login;
    private String senha;
    @Inject
    private UsuarioDAO usuarioDAO;

    public LoginBean() {
    }

    @PostConstruct
    public void init() {
        this.login = "";
        this.senha = "";
        usuarioDAO.salvar(new Usuario("Benigno Sales", "benigno", "1"));
        usuarioDAO.salvar(new Usuario("Gabriela Sales", "gabriela", "2"));
        usuarioDAO.salvar(new Usuario("Letícia Sales", "leticia", "3"));
    }

    public String logIn() {
        usuarioLogado = usuarioDAO.buscar(login, senha);
        if (usuarioLogado == null) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Usuário ou Senha Inválidos", "Login Inválido"));
            return null;
        } else {
            HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            if (session != null) {
                session.setAttribute("usuario", usuarioLogado);
            }
            return "/index?faces-redirect=true";
        }
    }

    public String logOff() {
        FacesContext fc = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) fc.getExternalContext().getSession(false);
        session.invalidate();
        return "/login?faces-redirect=true";
    }

No método init, que é anotado com @PostConstruct, criamos três usuários para podermos fazer a demonstração. Verifique que no DAO você deverá fazer sua conexão com o banco de dados ou utilizar-se de algum framework ORM(Object/Relational Mapping) para realizar a persistência/consulta dos dados. Como o nosso foco não é persistência de dados, nosso exemplo criamos apenas um DAO que acrescenta/remove/consulta dados a um List.

O método logIn() vai ser chamado na nossa view login.xhtml pelo botão Log In e é através dele que iremos validar se o usuário é cadastrado e se seus dados coincidem com os dados digitados por quem esta tentando acessar o sistema. Neste método os campos login e senha são enviados através do método buscar para o DAO que deve verificar na lista de usuários se existe algum usuário cadastrado com os dados informados. No caso do usuário existir, o sistema libera o acesso e joga este usuário na sessão, além disso redireciona o usuário para a página index.xhtml. Se o usuário não for localizado, o sistema envia uma mensagem de Login Inválido.

O método logOff que é chamado pelo botão Sair, invalida a sessão o que faz com que todos os dados armazenados nela sejam apagado e depois redireciona o usuário à página login.xhtml.

Mas aí você se pergunta, e como é que a cada troca de página o sistema vai saber que eu estou logado e que sou um usuário válido? Aí é onde entra o PhaseListener.

A API do JSF define o as fases como etapas do ciclo de vida do framework. Existem 6 fases no JSF:

  1. Restore View
  2. Apply Request Values
  3. Process Validators
  4. Update Model
  5. Invoke Application
  6. Render Response

Os PhaseListener são objetos que serão notificados no início e término de cada fase, atuando assim como interceptadores, que permitem que você capture uma requisição para realizar algo específico, como por exemplo validar se existe um objeto na sessão. Abaixo veja como ficará o LoginPhaseListener.java que você deverá colocar no pacote br.com.benignosales.loginjsf.phaselistener.

public class LoginPhaseListener implements PhaseListener {

    private FacesContext facesContext;

    @Override
    public void afterPhase(PhaseEvent event) {
        facesContext = event.getFacesContext();
        String viewId = facesContext.getViewRoot().getViewId();

        NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
        boolean paginaLogin = viewId.lastIndexOf("login") > -1;

        if (existeUsuarioLogado() && paginaLogin) {
            nh.handleNavigation(facesContext, null, "/index?faces-redirect=true");
        } else if (!existeUsuarioLogado() && !paginaLogin) {
            nh.handleNavigation(facesContext, null, "/login?faces-redirect=true");
        }
    }

    public boolean existeUsuarioLogado() {
        return (((Usuario) getAtributoSessao("usuario")) != null);
    }

    public Object getAtributoSessao(String attributeName) {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        if (session != null) {
            return session.getAttribute(attributeName);
        }
        return null;
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        
    }

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

}

 

Temos o método afterPhase que é chamado após cada evento e que é nele onde iremos realizar nosso procedimento de verificação de acesso ao sistema. Verificamos neste método se existe usuário logado e se estivermos na página de login, então redirecione para a página index. A próxima verificação que fazemos é se não estivermos na página de login e nenhum usuário estiver na sessão, então redirecione o usuário para a página de login para que ele insira as suas credenciais e, assim, tenha acesso ao sistema.

Para completar nosso tutorial, coloco o código das duas views que utilizaremos, index.xhtml e login.xhtml.

index.html

<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">
    <h:head>
        <title>Benigno Sales - Exemplo Login - Página Inicial</title>         
        <h:outputStylesheet library="css" name="logincss.css"/>
    </h:head>
    <h:body>
        <p:dialog modal="true" visible="true" width="400" height="280" header="Acessar o Sistema" showHeader="false" resizable="false">
            <h:form id="formLogoff">
                <h:panelGrid cellspacing="10">
                    <h:graphicImage library="img" name="logo.png"/>         
                    <h:outputText value="Seja Bem-vindo #{loginBean.usuarioLogado.nome}! Agora você possui acesso ao Site"/>                    
                    <p:commandButton value="Sair" action="#{loginBean.logOff()}"/>
                </h:panelGrid>
            </h:form>
        </p:dialog>
    </h:body>
</html>

login.xhtml

<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">
    <h:head>
        <title>Blog Benigno Sales - Exemplo Login - Página Inicial</title>         
        <h:outputStylesheet library="css" name="logincss.css"/>
    </h:head>
    <h:body>
        <p:dialog visible="true" width="400" height="500" header="Acessar o Sistema" showHeader="false" modal="true" resizable="false">            
            <h:panelGrid style="margin: 0 auto;"><h:outputText styleClass="fa fa-lock fa-5x"/></h:panelGrid>
            <h:form id="formLogin">
                <h:panelGrid cellspacing="10">
                    Digite seu usuário e senha para ter acesso ao sistema:                    
                    <p:messages id="messagesLogin"/>
                    <p:inputText placeholder="Usuário" value="#{loginBean.login}" size="40" required="true" requiredMessage="Campo Login é obrigatório"/>
                    <p:password placeholder="Senha" value="#{loginBean.senha}" size="40" required="true" requiredMessage="Campo Senha é obrigatório"/>
                    <p:commandButton value="Log In" action="#{loginBean.logIn()}" update="messagesLogin"/>
                </h:panelGrid>
            </h:form>
        </p:dialog>
    </h:body>
</html>

Abaixo o vídeo do exemplo funcionando:

15 comentários em “Criando uma Página de Login Completa com JSF e Primefaces”

  1. Edson disse:

    Excelente “tutu” Benigno. Parabéns pela iniciativa em distribuir seu vasto conhecimento com a comunidade desenvolvedora.

    1. Benigno disse:

      Grande Edson, 🙂 abraço!

  2. Renan disse:

    onde está a classe DAO ?

    1. Benigno disse:

      Geralmente não posto o DAO, pois depende de como você irá persistir os dados. Pode utilizar JPA ou não, por isso fica a critério de quem implementa.

  3. Annie disse:

    Em primeiro lugar obrigada por disponibilizar o código.
    Tem como você mostrar o DAO do usuário?

    1. Benigno disse:

      Geralmente não posto o DAO, pois depende de como você irá persistir os dados. Pode utilizar JPA ou não, por isso fica a critério de quem implementa.

  4. Ayrton Pereira disse:

    Boas Benigno, bom tutorial. Tenho uma questão estou desenvolvendo um projecto para gestão de stock, e preciso desenvolver a parte de login. A minha questão é como posso adaptar a parte de LoginBean, visto que em todo o meu projecto usei ManagedBean.

    1. Benigno disse:

      Altere a classe LoginBean com a anotação @ManagedBean e use a importação do jsf.
      import javax.faces.bean.ManagedBean;

      Mesma coisa para o escopo que for usar:
      Para @ViewScoped use import javax.faces.bean.ViewScoped;

  5. Ayrton Pereira disse:

    Boas benigno! Bom tutorial, não tenho a certeza se o primeiro comentário seguio mas, eu tenho uma questão. Estou a desenvolver um projecto para gestão de stock e precisamente estou a começar a desenvolver a parte de login A minha questão é como posso adaptar o que pude ver no tutorial visto que estou a usar em todo o projecto o @ManagedBean, penso que devia adaptar a classe Bean certo? Obrigado aguardo uma resposta Ayrton

  6. Benigno disse:

    Você pode criar:

    Cria um array com as páginas permitidas e então colocaria a verificação:


    String pagina = viewId.substring(viewId.lastIndexOf("/")+1, viewId.indexOf("."));
    boolean permitido = paginasPermitidas.contains(pagina);
    ...
    else if (!existeUsuarioLogado() && !paginaLogin && !permitido) {
    nh.handleNavigation(facesContext, null, “/login?faces-redirect=true”);
    }

  7. ErikPley disse:

    Cadê o arquivo css?

  8. Alberto disse:

    Olá! É possível disponibilizar o logincss.css?

  9. ROGERIO FRANCISCO PEREIRA LIMA disse:

    qual o usuario e a senha

  10. Gostaria de saber se tem algum exemplo de agenda com list de horários

  11. Gostei da sua postagem, gostaria de receber mais postagem desse tipo.

Escreva uma resposta ou comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *