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.

Tela para criação de um novo banco de dados
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.

Configuração do Nome do Projeto
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.

'Layout de como devem estar as pastas e as classes Java dentro do projeto dao
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.

Tela para escolha do servidor que será executado o projeto
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.

Configuração de Implantação do Filtro
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.

Hierarquia do projeto Web
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> ${erro} </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).

Tela inicial do sistema sendo executado
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.

Página inicial com mensagem de erro
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.