Você está na página 1de 9

JSF Filter: Criando um sistema de login com

criptografia MD5
Veja neste artigo como criar um sistema de login utilizando a criptografia MD5 e JSF 2.0 e
Filter.

Neste artigo veremos como construir um sistema de login com JSF 2.0 utilizando Filters. Nosso sistema de
login contar ainda com um nvel a mais de segurana, implementando a criptografia MD5 nas senhas,
evitando assim que as mesmas possam ser visualizadas por qualquer um.
Em JSF, o Filter um recurso que possibilita o gerenciamento de todas as requisies HTTP do seu
servidor, filtrando o endereo que est sendo acessado. Sendo assim, quando o usurio Joo acessar aquela
URL que proibida, voc pode imediatamente redirecion-lo para outro endereo, antes que a resposta seja
dada ao cliente.
O MD5 ou Message-Digest Algorithm 5 um algoritmo hash de 128bits unidirecional e pelo simples fato de
ser unidirecional no h como decriptar o mesmo, ou seja, se voc criptografar determinada senha em MD5,
no ter como fazer o processo inverso, que seria descobrir a senha contida no MD5. Ento se no h como
decriptar um hash MD5, como saberemos se a senha que o usurio digitou est correta? Pense um pouco,
ns podemos criptografar a senha digitada pelo usurio para MD5 e simplesmente comparar os dois hash
MD5, sendo assim, se os dois forem iguais, saberemos que a senha est correta. Mas caso o usurio esquea
a senha, no h maneira de recuper-la, apenas gerar uma nova senha. por esse motivo que em muitos
sistemas a recuperao da senha na verdade a gerao de uma nova senha.
Criptografar a senha em MD5 lhe d muitos pontos em segurana, confiabilidade e qualidade. Comeando
pelo fato de que qualquer pessoa que tiver acesso ao banco de dados no poder visualizar as senhas de
nenhum usurio, pois imagine se o usurio joao2014 utiliza sua senha para outras coisas como: bank
online, e-mail, facebook e etc.
Por isso, a senha do usurio deve ser uma informao sigilosa que nem o desenvolvedor deve ter
conhecimento, por uma questo simples de tica profissional. Existem ainda outros algoritmos HASH para
criptografar informaes, mas no nosso foco estud-los.
Construindo sistema de login

Alm do JSF, trabalharemos com outros frameworks para nos auxiliar no desenvolvimento desta aplicao,
mas no obrigatoriedade us-los, pois voc pode adaptar o mesmo para a sua realidade. Usaremos ento o
JPA/Hibernate, Spring Framework e o nosso banco de dados ser o PostgreSQL, mas fique a vontade para
escolher outro de sua preferncia. Lembrando que no mostraremos configuraes bsicas de JPA ou
mesmo Spring, j que estamos partindo do principio que o foco deste artigo mostrar a construo de um
Login com Filter.
Observe na Listagem 1 a criao da tabela usuario.
Listagem 1. Criao da Tabela
CREATE TABLE usuario
(
id serial NOT NULL,
data_cadastro date,
email character varying(255),

nome character varying(255),


senha character varying(255),
CONSTRAINT usuario_pkey PRIMARY KEY (id ),
CONSTRAINT usuario_email_key UNIQUE (email )
)

Criada a tabela acima em nosso banco de dados, precisamos criar nossa classe Usuario, que ficar como
definido na Listagem 2.
Listagem 2. Criando Classe Usuario
import java.util.Date;
import
import
import
import
import
import
import
import

javax.persistence.Column;
javax.persistence.Entity;
javax.persistence.NamedQueries;
javax.persistence.NamedQuery;
javax.persistence.Table;
javax.persistence.Temporal;
javax.persistence.TemporalType;
javax.persistence.Transient;

@Entity
@NamedQueries(value = { @NamedQuery(name = "Usuario.findByEmailSenha",
query = "SELECT c FROM Usuario c "
+ "WHERE c.email = :email AND c.senha = :senha")})
@Table(name = "usuario")
public class Usuario {
/**
*
*/
private static final long serialVersionUID = 1L;
@Transient
public static final String FIND_BY_EMAIL_SENHA =
"Usuario.findByEmailSenha";
@Id
@GeneratedValue(strategy =
javax.persistence.GenerationType.IDENTITY)
private Integer id;
@Column
private String nome;
@Column(unique = true)
private String email;
@Column
private String senha;
@Column(name = "data_cadastro")
@Temporal(TemporalType.DATE)
private Date dataCadastro;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}

public String getNome() {


return nome;
}
public void setNome(String nome) {
this.nome = nome.trim();
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email.trim().toLowerCase();
}
public String getSenha() {
return senha;
}
public void setSenha(String senha) {
this.senha = senha.trim();
}
public Date getDataCadastro() {
return dataCadastro;
}
public void setDataCadastro(Date dataCadastro) {
this.dataCadastro = dataCadastro;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 :
id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
return (obj instanceof AbstractBean) ?
(this.getId() == null ? this == obj :
this.getId().equals((
(AbstractBean)obj).getId())):false;
}
}

Nossa classe completa e possui todas as notaes JPA necessrias, juntamente com os mtodos equals() e
hashCode() e as namedQueries que nos sero teis para pesquisar os usurios no banco e dados.
Como dissemos anteriormente, se fossemos mostrar detalhes da construo de cada parte do sistema com
por exemplo: DAO, (Data Access Object), BO (Bussiness Object) e Configuraes, nosso artigo perderia o
3

foco, ento mostraremos agora os mtodos de verificao de login presentes em nosso BO, chamado
UsuarioBOImpl (Listagem 3).
Listagem 3. Mtodo de validao do usurio no UsuarioBOImpl
// Verifica se usurio existe ou se pode logar
public Usuario isUsuarioReadyToLogin(String email, String senha) {
try {
email = email.toLowerCase().trim();
logger.info("Verificando login do usurio " + email);
List retorno = dao.findByNamedQuery(
Usuario.FIND_BY_EMAIL_SENHA,
new NamedParams("email", email
.trim(), "senha", convertStringToMd5(senha)));
if (retorno.size() == 1) {
Usuario userFound = (Usuario) retorno.get(0);
return userFound;
}
return null;
} catch (DAOException e) {
e.printStackTrace();
throw new BOException(e.getMessage());
}
}

Bom, nosso mtodo recebe como parmetro um Email e Senha, que so passados para o DAO utilizando
aquela NamedQuery chamada findByEmailSenha que definimos em nosso Bean Usuario. O importante
aqui perceber duas coisas:
1. A senha que passada por parmetro no est criptografada, sendo assim, no conseguiramos comparar
com a senha no banco. Ento antes de passar o parmetro ao DAO, convertemos a senha para MD5 com o
mtodo convertStringToMD5(senha).
2. Caso esse retorno do DAO seja uma Lista com um elemento, significa que o usurio foi encontrado no banco
e retornamos o mesmo, caso contrrio o retorno ser null.

Veja na Listagem 4 como nosso mtodo para converter de String para MD5.
Listagem 4. Mtodo conversor de String para MD5
private String convertStringToMd5(String valor) {
MessageDigest mDigest;
try {
//Instanciamos o nosso HASH MD5, poderamos usar outro como
//SHA, por exemplo, mas optamos por MD5.
mDigest = MessageDigest.getInstance("MD5");
//Convert a String valor para um array de bytes em MD5
byte[] valorMD5 = mDigest.digest(valor.getBytes("UTF-8"));
//Convertemos os bytes para hexadecimal, assim podemos salvar
//no banco para posterior comparao se senhas
StringBuffer sb = new StringBuffer();
for (byte b : valorMD5){
sb.append(Integer.toHexString((b & 0xFF) |
0x100).substring(1,3));
}
return sb.toString();

} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}

Ento agora temos dois mtodos importantes para nossa aplicao, no BO. Um para verificar se o usurio
vlido e outro para converter a senha para MD5. O prximo passo criar um ManagedBean que comunicar
a pgina XHTML de Login com o nosso BO, que se UsuarioMBImpl, e tambm mostraremos apenas os
mtodos importantes. Observe a Listagem 5.
Listagem 5. ManagedBean para Login do Usurio
//True se usurio est logado e false caso contrrio
private boolean loggedIn;
//Armazena o usurio logado
private Usuario usuarioLogado;
//Email e senha digitado pelo usurio na pgina XHTML
private String email, senha;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSenha() {
return senha;
}
public void setSenha(String senha) {
this.senha = senha;
}
//Realiza o login caso de tudo certo
public String doLogin(){
//Verifica se o e-mail e senha existem e se o usuario pode logar
Usuario usuarioFound = (Usuario)
usuarioBO.isUsuarioReadyToLogin(email, senha);
//Caso no tenha retornado nenhum usuario, ento mostramos um erro
//e redirecionamos ele para a pgina login.xhtml
//para ele realiza-lo novamente
if (usuarioFound == null){
addErrorMessage("Email ou Senha errado, tente novamente !");
FacesContext.getCurrentInstance().validationFailed();
return "/login/login.xhtml?faces-redirect=true";
}else{
//caso tenha retornado um usuario, setamos a varivel loggedIn
//como true e guardamos o usuario encontrado na varivel
//usuarioLogado. Depois de tudo, mandamos o usurio
//para a pgina index.xhtml

loggedIn = true;
usuarioLogado = usuarioFound;
return "/restricted/index.xhtml?faces-redirect=true";
}
}
//Realiza o logout do usurio logado
public String doLogout(){
//Setamos a varivel usuarioLogado como nulo, ou seja, limpamos
//os dados do usurio que estava logado e depois setamos a varivel
//loggedIn como false para sinalizar que o usurio no est mais
//logado
usuarioLogado = null;
loggedIn = false;
//Mostramos
um
mensagem
ao
usurio
e
redirecionamos
ele
//pgina de login
addInfoMessage("Logout realizado com sucesso !");
return "/login/login.xhtml?faces-redirect=true";
}

para

No cdigo acima temos uma chamada ao nosso mtodo isUsuarioReadyToLogin() que est no nosso BO
criado anteriormente. Caso a instncia da varivel usuarioFound seja nula, significa que no foi
encontrado nenhum usurio na base, ento simplesmente retornamos um erro ao usurio e redirecionamos o
mesmo para a pgina de login novamente. Caso seja encontrado algum usurio setamos a varivel
loggedIn como true, guardamos os dados do usurio logado na varivel usuarioLogado e redirecionamos
ele para o index.xhtml, ou seja, a pgina de bem vindo.
O mtodo de logout simples, apenas fazemos o inverso que fizemos no mtodo de login, setando o
loggedIn como false e o usuarioLogado como nulo.
Vamos ver agora nossa pgina XHTML de Login, conforme a Listagem 6.
Listagem 6. login.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions">
<h:head>
<h:outputStylesheet library="css" name="login.css" />
</h:head>
<h:body>
<h:form id="formLogin" enctype="multipart/form-data">
<p:growl autoUpdate="true" id="messages" />
<p:panelGrid styleClass="semBorda" columns="2">
<h:outputText value="Email: " />
<p:inputText value="#{usuarioMB.email}"
styleClass="lowercase"
size="35" required="true"
requiredMessage="O Email obrigatrio" />
<h:outputText value="Senha: " />
<p:password value="#{usuarioMB.senha}"

size="35" required="true"
requiredMessage="A Senha obrigatria" />
</p:panelGrid>
<p:panelGrid columns="2" styleClass="semBorda">
<p:commandButton icon="ui-icon-unlocked"
value="Entrar"
action="#{usuarioMB.doLogin}" />
<p:commandButton icon="ui-icon-mail-closed"
value="Recuperar Senha"
action="#{usuarioMB.doLogin}" />
</p:panelGrid>
</h:form>
</h:body>
</html>

Temos ento quase todo mecanismo pronto:


1.
2.
3.
4.

A pgina de login
A comunicao do XHTML com o BO atravs do ManagedBean
As tabelas do banco e o mapeamento via JPA no Java da nossa classe Usuario
As validaes e converses no BO.

Falta agora o principal, que criar o Filter para direcionar o usurio para o local certo, ento comearemos
definindo o filter no arquivo web.xml, de acordo com a Listagem 7.
Listagem 7. Definindo filter no web.xml
<!-- login filter -->
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>br.com.meuprojeto.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/restricted/*</url-pattern>
</filter-mapping>

Acima estamos definindo duas coisas:


1. Dizemos atravs da tag <filter> que a nossa classe responsvel por realizar o controle do filtro fica em
br.com.meuprojeto.LoginFilter e chama-se LoginFilter.
2. Atravs da tag <filter-mapping> dizemos que o LoginFilter (definido atravs do <filter-name>) deve
interceptar todas as requisies que passam por /restricted/*, ou seja, tudo que estiver dentro do
diretrio restricted ser redirecionado para o LoginFilter que tomar alguma deciso ou simplesmente
mandar prosseguir com a requisio. Este o conceito chave, ento entenda que se voc acessar
/restricted/paginabbbb.xhtml automaticamente voc ser enviado para o LoginFilter, claro que de forma
imperceptvel.

Ento finalmente nosso LoginFilter ser como o da Listagem 8.


Listagem 8. LoginFilter
import java.io.IOException;
import javax.servlet.Filter;

import
import
import
import
import
import
import

javax.servlet.FilterChain;
javax.servlet.FilterConfig;
javax.servlet.ServletException;
javax.servlet.ServletRequest;
javax.servlet.ServletResponse;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;

import br.com.meuprojeto.mb.UsuarioMBImpl;
public class LoginFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//Captura o ManagedBean chamado usuarioMB
UsuarioMBImpl usuarioMB = (UsuarioMBImpl)
((HttpServletRequest) request)
.getSession().getAttribute("usuarioMB");
//Verifica se nosso ManagedBean ainda no
//foi instanciado ou caso a
//varivel loggedIn seja false, assim saberemos que
// o usurio no est logado
if (usuarioMB == null || !usuarioMB.isLoggedIn()) {
String contextPath = ((HttpServletRequest) request)
.getContextPath();
//Redirecionamos o usurio imediatamente
//para a pgina de login.xhtml
((HttpServletResponse) response).sendRedirect
(contextPath + "/login/login.xhtml");
} else {
//Caso ele esteja logado, apenas deixamos
//que o fluxo continue
chain.doFilter(request, response);
}
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}

Fizemos questo de mostrar toda a classe LoginFilter para que voc possa perceber a sua totalidade. Veja
que a nica funo desta classe (neste exemplo simples) mandar o usurio para a pgina de login.xhtml ou
mandar ele prosseguir com a requisio atravs do chain.doFilter.
Recuperao de senha

Como bnus a este artigo, decidimos acrescentar mais um mtodo muito til para que voc possa
implementar a gerao de novas senhas automaticamente. Como voc est trabalhando com senhas
criptografadas em MD5, no h a possibilidade de recuperar uma senha perdida, ou seja, aquela senha que o
usurio por algum motivo esqueceu.
8

A nica forma de acessar o sistema novamente gerando uma nova senha para este usurio. Ento
sugerimos o mtodo da Listagem 9, mas fique a vontade para adicionar a complexidade que achar
necessria ao mesmo.
Listagem 9. Mtodo gerador de senhas
public String gerarNovaSenha() {
String[] carct = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
"m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z" };
String senha = "";
for (int x = 0; x < 10; x++) {
int j = (int) (Math.random() * carct.length);
senha += carct[j];
}
return senha;
}

Voc pode utilizar o mtodo acima gerando uma nova senha para o usurio e enviado ao seu e-mail ou
mesmo mostrando diretamente na tela, o que no muito seguro.
Com essa aplicao possvel criar uma sistema de login poderoso e robusto, obviamente que realizando
algumas modificaes como, por exemplo, a adio de Perfis de Usurio.
Veja como torna-se simples controlar o que o usurio est fazendo com nosso LoginFilter, pois temos a
URL para onde ele deseja ir, cabe a ns decidir se ele deve ou no continuar. Poderamos at criar um log de
todos os acessos em cada URL, na hora e minuto exato que ele acessou e muitos outros recursos.
Para finalizar, importante salientar que existem outros mtodos para implementao de um sistema de
login, frameworks com o Spring Security ou o JAAS e etc. Mas um bom filter pode realizar tarefas to
robustas quanto, s depende do nvel de complexidade adotado.

Você também pode gostar