JavaServer Pages: Autenticação de login com JSP e PostgreSQL

Veja nesse artigo como criar um sistema de autenticação de usuário com login e senha utilizando a tecnologia JSP e o banco de dados PostgreSQL

JSP (JavaServer Pages) é um mecanismo que auxilia os desenvolvedores a implementarem páginas para internet no formato HTML ou em outros tipos, geradas de forma automaticamente e dinâmica. Nesse artigo o ambiente de desenvolvimento a ser utilizado é o Netbeans, pois nele, além de existir o servidor para a execução da aplicação, existem drivers específicos para a conectividade de dados como, por exemplo, a utilização PostgreSQL, que agiliza muito o envio e a troca de informações entre o banco de dados criado e a aplicação web. O objetivo desse artigo é criar um sistema de autenticação básico no qual serão implementadas duas páginas: uma de login e senha (entrada) no estilo padrão e a outra página principal com a opção de sair do sistema.

Para manipularmos o banco de dados será necessário o SGBD mais utilizado para o PostgreSQL, que é o pgAdmin. A instalação do mesmo é muito simples, por isso, partindo que ele já esteja instalado em sua máquina e aberto, será preciso criar um novo banco de dados com o nome Autentica. Para criar, basta clicar com o botão direito em Databases localizado no Object Explorer e depois em New Database. Aparecerá uma janela de acordo com a Figura 1.

Figura 1. Tela para criação de um novo banco de dados

Basta colocar no campo Name “Autentica” e clicar em OK que será criado.

Agora será criado uma tabela referente a Usuario e em seguida os campos e suas propriedades.

Para isso, é necessário clicar em clima do banco de dados criado e em seguida clicar no ícone Execute arbitrary SQL queries, localizado abaixo da barra de menus e colocar o código para a criação da tabela e seus campos, de acordo com a Listagem 1.

CREATE TABLE usuario ( usu_codigo integer NOT NULL, usu_nome character varying(100) NOT NULL, usu_login character varying(6) NOT NULL, usu_senha character varying(6) NOT NULL, usu_adm boolean NOT NULL, CONSTRAINT usuario_pkey PRIMARY KEY (usu_codigo), CONSTRAINT usuario_usu_login_key UNIQUE (usu_login) ) WITH ( OIDS=FALSE ); ALTER TABLE usuario OWNER TO postgres;
Listagem 1. Comando para a criação da tabela usuário, seus campos e propriedades

Para concluir, basta clicar no botão Execute Query (uma seta apontada para a direita), localizado abaixo da barra de menus, ou se preferir, aperte a tecla de atalho F5 para terminar de criar a tabela.

Para adicionar alguns registros na tabela, coloque os comandos descritos na Listagem 2.

insert into usuario (usu_codigo, usu_nome, usu_login, usu_senha, usu_adm) values (1, 'Administrador', 'admin', '123456', true); insert into usuario (usu_codigo, usu_nome, usu_login, usu_senha, usu_adm) values (2, 'Convidado', 'guest', 'guest', false);
Listagem 2. Comandos em SQL para inserção de registros

Objeto de Acesso a Dados (DAO)

Agora será criado um Objeto de Acesso a Dados (DAO) que servirá para que futuramente a aplicação web possa consumir todos os recursos presentes nesse objeto que por consequência se transformará em uma biblioteca.

Com o Netbeans instalado e aberto, vá em Arquivo → Novo Projeto e selecione Java. Clique em próximo para aparecer a janela de configurações, como mostra a Figura 2.

Figura 2. Configuração do Nome do Projeto

Coloque no campo nome como ‘dao” e no campo localização do Projeto coloque o caminho ‘D:\’. Não esqueça de desmarcar a opção Criar Classe Principal.

Em seguida basta clicar em Finalizar que o projeto dao estará pronto para ser implementado.

A proposta agora é criar três pastas: sql, entidades e ao.

Dentro da pasta sql será criada a nova classe Java com o nome de Conexão.java. Dentro da pasta entidades será criada a nova classe Java com o nome de Usuario.java e dentro da pasta dao será criada uma interface com o nome DAO.java e duas classes com os nomes DAOException.java e Usuario.java. Para vermos como ficará toda essa criação, o layout deve estar parecido com a Figura 3.

Figura 3. Layout de como devem estar as pastas e as classes Java dentro do projeto dao

Dentro da classe Conexao.java será implementado o código descritos na Listagem 3.

package sql; import java.sql.*; import java.util.logging.Level; import java.util.logging.Logger; public final class Conexao { private static final String usuario = "postgres"; private static final String senha = "postgres123"; private static final String url = "jdbc:postgresql://127.0.0.1:5432/Autentica"; public static Connection open() { try { Class.forName("org.postgresql.Driver"); return DriverManager.getConnection(url, usuario, senha); } catch (SQLException | ClassNotFoundException ex) { Logger.getLogger(Conexao.class.getName()).log(Level.SEVERE, null, ex); return null; } } public static void close(ResultSet rs, Statement st, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { } } if (st != null) { try { st.close(); } catch (SQLException e) { } } if (conn != null) { try { conn.close(); } catch (SQLException e) { } } } public static void close(Statement st, Connection conn) { close(null, st, conn); } public static void close(Connection conn) { close(null, null, conn); } }
Listagem 3. Classe responsável pela conexão com o banco de dados Autentica

Dentro da classe Usuario.java será implementada os códigos descrito na Listagem 4. Essa classe terá como objetivo ser o modelo da aplicação, com as propriedades dos campos, receber e definir os dados de acordo com seu tipo, encapsulando-os e também comparar o que está salvo com os dados informados pelo usuário.

package entidades; import java.io.Serializable; import java.util.Objects; public class Usuario implements Serializable { private Integer codigo; private String nome; private String login; private String senha; private boolean administrador; public Usuario() { this(null, null, null, null, false); } public Usuario(Integer codigo, String nome, String login, String senha, boolean administrador) { this.codigo = codigo; this.nome = nome; this.login = login; this.senha = senha; this.administrador = administrador; } public Integer getCodigo() { return codigo; } public void setCodigo(Integer codigo) { this.codigo = codigo; } 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 = senha; } public boolean isAdministrador() { return administrador; } public void setAdministrador(boolean administrador) { this.administrador = administrador; } @Override public int hashCode() { int hash = 7; hash = 89 * hash + Objects.hashCode(this.codigo); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Usuario other = (Usuario) obj; if (!Objects.equals(this.codigo, other.codigo)) { return false; } return true; } }
Listagem 4. Classe Model

Dentro da interface DAO.java será implementada os códigos descritos na Listagem 5.

package dao; import java.util.List; public interface DAO<T> { public T getSingle(Object... chave); public List<T> getList(); public List<T> getList(int top); }
Listagem 5. Interface responsável por conter métodos para recuperar as informações de diversas maneiras

E dentro da classe DAOException.java será implementada o código descrito na Listagem 6.

package dao; public class DAOException extends Exception { public DAOException(Throwable cause) { super(cause); } public DAOException(String message, Throwable cause) { super(message, cause); } public DAOException(String message) { super(message); } public DAOException() { super(); } }
Listagem 6. Classe responsável por lançar exceções durante a aplicação, caso haja algum imprevisto

Dentro da classe UsuarioDAO.java será implementada os códigos descrito na Listagem 7. Essa classe possui diversos métodos e cada um é responsável por buscar o registro de uma forma diferente.

package dao; import entidades.Usuario; import sql.Conexao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class UsuarioDAO implements DAO<Usuario> { public Usuario getSingle(String login) { Connection conn = Conexao.open(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement("select usu_codigo, usu_nome, usu_login, usu_senha, usu_adm from usuario where usu_login = ?"); ps.setString(1, login); rs = ps.executeQuery(); if (rs.next()) { return new Usuario(rs.getInt("usu_codigo"), rs.getString("usu_nome"), rs.getString("usu_login"), rs.getString("usu_senha"), rs.getBoolean("usu_adm")); } } catch (SQLException ex) { } finally { Conexao.close(rs, ps, conn); } return null; } @Override public Usuario getSingle(Object... chave) { if (chave[0] instanceof Integer) { Connection conn = Conexao.open(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement("select usu_codigo, usu_nome, usu_login, usu_senha, usu_adm from usuario where usu_codigo = ?"); ps.setInt(1, (Integer) chave[0]); rs = ps.executeQuery(); if (rs.next()) { return new Usuario(rs.getInt("usu_codigo"), rs.getString("usu_nome"), rs.getString("usu_login"), rs.getString("usu_senha"), rs.getBoolean("usu_adm")); } } catch (SQLException ex) { } finally { Conexao.close(rs, ps, conn); } } return null; } @Override public List<Usuario> getList() { return getList(0); } @Override public List<Usuario> getList(int top) { if (top < 0) { return null; } List<Usuario> lista = null; Connection conn = Conexao.open(); Statement ps = null; ResultSet rs = null; try { ps = conn.createStatement(); rs = ps.executeQuery("select " + (top > 0 ? "top " + top : "") + "usu_codigo, usu_nome, usu_login, usu_senha, usu_adm from usuario"); lista = new ArrayList<>(); while (rs.next()) { lista.add(new Usuario(rs.getInt("usu_codigo"), rs.getString("usu_nome"), rs.getString("usu_login"), rs.getString("usu_senha"), rs.getBoolean("usu_adm"))); } } catch (SQLException ex) { } finally { Conexao.close(rs, ps, conn); } return lista; } }
Listagem 7. Classe UsusarioDAO

Criando uma Nova Aplicação Web

Agora será criada uma nova aplicação web. Para isso, basta ir à barra de menus, em ArquivoNovo Projeto. Selecione a categoria Java Web e o Projeto Aplicação Web. Depois clique em Próximo e aparecerá uma janela para definir o nome do projeto assim como fizermos anteriorente.

O resultado da configuração do servidor será o mesmo que o apresentado pela Figura 4.

Figura 4. Tela para escolha do servidor que será executado o projeto

Por padrão, será selecionado o Apache Tomcat, mas poderia estar selecionado o GlassFish Server. A versão do Java EE que está selecionada a última disponível no site da tecnologia. O caminho do Contexto pode deixar do jeito que foi definido automaticamente. Ao final clique em Finalizar para que o “esqueleto” do projeto seja criado.

Implementação do Projeto

Antes de criarmos as pastas de organização para o projeto, adicionaremos o projeto DAO desenvolvido anteriormente. Para isso, basta clicar com o botão direito em Biblioteca e depois em: “Adicionar Projeto”. Selecione o projeto criado e depois clique em Adicionar Arquivos JAR de Projeto para finalizar a importação.

Concluindo, é necessário importar o driver JDBC pronto e próprio para o PostgreSQL, com o objetivo de conectividade com o banco de dados. Com isso, clique com o botão direito em Biblioteca e selecione a opção Driver JDBC do PostgreSQL. Clique em Finalizar que o driver será adicionado automaticamente.

Agora definiremos as pastas para melhor organização do projeto, com a finalidade de não haver perca de caminhos, arquivos ou de trocas de informações.

Então, dentro da pasta Pacotes de Códigos-fonte será proposto colocar quatro Pacotes Java: filtro, logado, servlet, útil.

No pacote filtro introduza um novo “Filtro”, clicando com o botão direito em cima do pacote, vá para a opção: Outros, selecione a categoria: Web e o Tipo de Arquivo: Filtro. Clique em Próximo e coloque o nome para o novo filtro de UsuarioLogado. Após clicar em Próximo, aparecerá uma janela de configuração de implantação do filtro: clique em novo e deixe configurado do mesmo jeito que se encontra a Figura 5.

Figura 5. Configuração de Implantação do Filtro

Após isso, clique em OK e depois em Finalizar.

Já no filtro criado UsuarioLogado.java, sobrescreva o seu conteúdo pelo código que se encontra na Listagem 8.

package filtro; import entidades.Usuario; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebFilter(filterName = "UsuarioLogado", urlPatterns = {"/logado/*"}) public class UsuarioLogado implements Filter { private String contextPath; public UsuarioLogado() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession(); Usuario u = (Usuario) session.getAttribute("usuarioLogado"); if (u == null) { session.invalidate(); res.sendRedirect(contextPath + "/index.jsp"); } else { res.setHeader("Cache-control", "no-cache, no-store"); res.setHeader("Pragma", "no-cache"); res.setHeader("Expires", "-1"); chain.doFilter(request, response); } } @Override public void destroy() { } @Override public void init(FilterConfig filterConfig) { this.contextPath = filterConfig.getServletContext().getContextPath(); } }
Listagem 8. Filtro UsuarioLogado

Esse filtro é responsável por controlar todas as ações durante a autenticação do sistema, tornando as requisições e o acesso para as páginas web mais refinada.

Dentro do pacote logado será criada uma nova classe Java com o nome de Menu.java e terá o mesmo código que o mesmo encontrado na Listagem 9.

package logado; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "Menu", urlPatterns = {"/logado/menu.jsp"}) public class Menu extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher rd = request.getRequestDispatcher( "/WEB-INF/view/logado/menu.jsp"); rd.forward(request, response); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override public String getServletInfo() { return "Short description"; } }
Listagem 9. Classe Menu

Esse código responsável por definir os modos de requisições para a página principal e redirecionamentos caso a autenticação esteja concluída e o usuário esteja logado no sistema.

Dentro do pacote servlet será criado uma nova classe chamada Index.java e será implementada com os códigos descritos na Listagem 10.

package servlet; import dao.UsuarioDAO; import entidades.Usuario; import util.Erro; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "Index", urlPatterns = {"/index.jsp", "/logout.jsp"}) public class Index extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Erro erros = new Erro(); if (request.getParameter("bOK") != null) { String login = request.getParameter("login"); String senha = request.getParameter("senha"); if (login == null || login.isEmpty()) { erros.add("Login não informado!"); } if (senha == null || senha.isEmpty()) { erros.add("Senha não informada!"); } if (!erros.isExisteErros()) { UsuarioDAO dao = new UsuarioDAO(); Usuario user = dao.getSingle(login); if (user != null) { if (user.getSenha().equalsIgnoreCase(senha)) { request.getSession().setAttribute("usuarioLogado", user); response.sendRedirect("logado/menu.jsp"); return; } else { erros.add("Senha inválida!"); } } else { erros.add("Usuário não encontrado!"); } } } request.getSession().invalidate(); request.setAttribute("mensagens", erros); String URL = "/WEB-INF/view/index.jsp"; RequestDispatcher rd = request.getRequestDispatcher(URL); rd.forward(request, response); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override public String getServletInfo() { return "Short description"; } }
Listagem 10. Classe index

Essa classe é responsável por tratar o envio e recebimento do redirecionamento das páginas e suas declarações, sendo armazenadas no servidor para que haja o processamento e enfim validação dos dados que o usuário informou, fazendo o tratamento de erros e adicionando para a propriedade de erros instanciada caso obtiver erro na autenticação.

E por fim, no pacote util será criada a classe Erro.java e será implementada os códigos descritos na Listagem 11.

package util; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public final class Erro implements Serializable { private final List<String> erros; public Erro() { erros = new ArrayList<>(); } public Erro(String mensagem) { erros = new ArrayList<>(); erros.add(mensagem); } public void add(String mensagem) { erros.add(mensagem); } public boolean isExisteErros() { return !erros.isEmpty(); } public List<String> getErros() { return erros; } }
Listagem 11. Classe Erro

Agora a proposta é apagar o arquivo index.html que foi criado automaticamente (por padrão) localizado dentro da pasta Páginas Web e criar uma nova pasta com o nome view para dentro da pasta WEB-INF. Dentro dessa pasta view será criada a página index, só que no formato .jsp. Além disso, crie outra pasta com o nome logado e dentro dessa crie um arquivo: .jsp com o nome de Menu. Para efeitos de design, dentro da pasta Páginas Web crie uma nova pasta chamada css e dentro crie um arquivo CSS. Para esse último passo, clique com o botão direito em cima da pasta criada: vá em Novo → Outros e na parte Categorias selecione HTML5. Logo após, em Tipos de Arquivos, selecione a opção Folha de Estilo em Cascata e defina o nome de layout. Ao clicar em Finalizar, a estrutura do projeto web deve ficar da mesma maneira que a demonstrada a Figura 6.

Figura 6. Hierarquia do projeto Web

Vamos agora preencher o arquivo menu.jsp com o código descrito na Listagem 12. Esse código HTML tags em JSP responsável é responsável por buscar o nome do usuário logado (autenticado) para a página principal. Ele contém também o link para o logout do sistema.

<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Menu do Sistema</title> </head> <body> <h1>Menu do Sistema</h1> <p>Olá ${sessionScope.usuarioLogado.nome}</p> <ul> <li> <a href="${pageContext.request.contextPath}/logout.jsp">Sair</a> </li> </ul> </body> </html>
Listagem 12. Menu.jsp

Dentro do arquivo index.jsp coloque os códigos descritos na Listagem 13.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Autenticação de Usuário</title> <link href="${pageContext.request.contextPath}/css/layout.css" rel="stylesheet" type="text/css"/> </head> <body> <h1>Autenticação de Usuário</h1> <c:if test="${mensagens.existeErros}"> <div id="erro"> <ul> <c:forEach var="erro" items="${mensagens.erros}"> <li> $ </li> </c:forEach> </ul> </div> </c:if> <form method="post" action="index.jsp"> <table> <tr> <th>Login: </th> <td><input type="text" name="login" value="${param.login}"/></td> </tr> <tr> <th>Senha: </th> <td><input type="password" name="senha" /></td> </tr> <tr> <td colspan="2"> <input type="submit" name="bOK" value="Entrar"/> </td> </tr> </table> </form> </body> </html>
Listagem 13. Index.jsp

Esse código HTML com JSP é responsável por exibir ao usuário os campos para inserção de login, senha e botão para autenticação. Caso o usuário informe dados incorretos ou não informe, é emitido uma mensagem no canto superior dizendo que existem erros.

E para finalizar, coloque dentro do arquivo layout.css o código descrito na Listagem 14.

#erro { width: 80%; margin: 0 auto; border: 1px solid red; background-color: beige; }
Listagem 14. Layout.css

O código é responsável por deixar a aparência da mensagem mais apresentável ao usuário.

Após ter finalizado, basta salvar o projeto e executar o mesmo apertando F6.

O resultado será um formulário simples de autenticação, como mostra a Figura 7(no Mozilla Firefox).

Figura 7. Tela inicial do sistema sendo executado

Para autenticar, basta colocar os dados que foram cadastrados na tabela anteriormente e depois clique em Entrar.

Figura 8. Página inicial com mensagem de erro

Caso o usuário não preencha os dados, então será apresentada uma mensagem de erro com a necessidade do sistema, por exemplo: se o Login e/ou a Senha não forem informados, como mostra a Figura 8.

Artigos relacionados