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:
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:
Excelente “tutu” Benigno. Parabéns pela iniciativa em distribuir seu vasto conhecimento com a comunidade desenvolvedora.
Grande Edson, 🙂 abraço!
onde está a classe DAO ?
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.
Em primeiro lugar obrigada por disponibilizar o código. Tem como você mostrar o DAO do usuário?
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.
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;
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
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”); }
Cadê o arquivo css?
Olá! É possível disponibilizar o logincss.css?
qual o usuario e a senha
Gostaria de saber se tem algum exemplo de agenda com list de horários
Gostei da sua postagem, gostaria de receber mais postagem desse tipo.
O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *
Comentário
Nome *
E-mail *
Site
Δ