O aplicativo desenvolvido no livro trabalha inteiramente com a senha do usuário aberta. Porém no tópico 6.8.2 damos uma dica de como configurar o Spring Security para trabalhar com a senha criptografada em MD5. O problema é que esta configuração não é suficiente para que o sistema inteiro trabalhe com a senha criptografada. Foi ai que o leitor Rafael Freitas entrou em contato e solicitou que fizessemos um post explicando como fazer esta alteração, o que faz todo o sentido.
Primeiro uma definição importante: a conhecido algoritmo Base64 na verdade é uma codificação e não criptografia, pois ele é reversível. Para ser considerada uma criptografia o algoritmo tem que ser inversível, o algoritmo MD5 tem esta capacidade.
De maneira macro, para que o sistema trabalhe com a senha do usuário criptografada algumas alterações são necessárias:
- Configuração do framework de segurança para trabalhar com a senha criptografada (já realizado no tópico 6.8.2).
- Decidir onde realizar a encriptação da senha do usuário
- Encriptar a senha do usuário
- Não exibir a senha criptografada em tela ao editar um usuário
Veremos agora o entendimento de cada alteração e depois a alteração passo a passo.
2 Decidir onde realizar a encriptação da senha do usuário
Seguindo o método utilizado no livro de sempre analisar e argumentar bem em qual camada uma alteração deve ser feita, vamos pensar sobre a alteração da senha criptografada.
A camada de acesso a dados (UsuarioDAO) deve ser imediatamente descartada, pois como já foi explicado esta camada apenas transfere dados do sistema para o banco de dados, não deve fazer qualquer alteração ou transformação.
A camada de regra de negócio (UsuarioRN) é uma forte candidata, mas como a senha do usuário será criptografada e esta criptografia é irreversível significa que a senha do usuário entrará nela aberta (ao salvar) e sairá criptografada (ao consultar). Acredito que isto pode causar alguma confusão. O ideal é que esta camada sempre trabalhe com a senha em um formato único.
Desta forma, a camada de visualização é o local correto para fazer a alteração e controlar a criptografia. Ela obrigatoriamente irá trabalhar com a senha aberta, pois é o que o usuário digita na tela, mas assim que receber esta senha deverá encriptar e enviar adiante para salvar, o que garante uma maior segurança.
3 Encriptar a senha do usuário
A encriptação da senha do usuário pode ser feita com uma classe do próprio Spring Security, conforme o exemplo a seguir:
import org.springframework.util.DigestUtils;
String senhaAberta = “teste123”;
String senhaCripto;
senhaCripto = DigestUtils.md5DigestAsHex(senhaAberta.getBytes());
System.out.println(senhaCripto);
Resultado
aa1bf4646de67fd9086cf6c79007026c
4 Não exibir a senha criptografada em tela ao editar um usuário
A partir do momento que o sistema começar a trabalhar com a senha criptografada não vai mais fazer sentido enviar esta senha para a tela, para alteração, como já é atualmente.
Não faz sentido pois pode causa confusão por dois motivos:
- A senha em MD5 é bem maior que a senha real digitada. Se o usuário digitar uma senha de 5 caracteres, em MD5 ela terá 32 caracteres. Se ao alterar um registro o usuário perceber esta senha bem maior vai estranhar e vai achar que está errado.
- Enviar a senha criptografada para a tela fará com que o campo de senha as vezes tenha uma senha aberta e outras vezes uma senha criptografada, com certeza é confuso.
Ou seja, se for o cadastro de um novo usuário a senha será preenchida normalmente, ser for uma alteração de usuário o campo senha deverá ser mantido em branco e o usuário somente preencherá este campo de quiser alterar a senha, senão deixa em branco.
Com isso definimos um comportamento do sistema: o campo de senha só será preenchido com senha aberta.
Porém se a senha for deixada em branco (significando que o usuário quer manter a senha antiga), como vamos salvar o registro do usuário se esta senha está em branco?
O segredo aqui é guardar a senha criptografada do usuário em outro local, quando a tela for aberta para edição (veremos como fazer no passo a passo).
Para manter o campo da senha em branco e considerar que ele só será preenchido quando o usuário quiser alterar existem duas opções:
1. Colocar um aviso junto ao campo dizendo: Preencher somente se quiser alterar.
2. Usar o componente do Primefaces WaterMark, que mostrará a mesma mensagem anterior, só que dentro do campo. http://www.primefaces.org/showcase/ui/watermark.jsf
Fazendo a alteração passo a passo
1. Crie uma nova propriedade String senhaCriptografada na classe UsuarioBean, gerando os métodos get e set.
2. Altere os campos de senha de tela para não serem obrigatórios caso seja uma edição de usuário. É possível identificar isto alterando o atributo required dos campo Senha e Confirmar Senha para:
required=”#{empty contextoBean.usuarioLogado}”
3. Altere os campos de senha da tela para não re-exibirem a senha, configurando o atributo redisplay=”false”.
4. Altere o método editar() para armazenar a senha atual na propriedade senhaCriptograda.
5. Altere o método salvar() para recuperar a senhaCriptografada (senha = senhaCriptografada) caso esta não senha sido preenchida em tela, e para criptografar a senha caso tenha sido preenchida em tela.
6. Inclua na tela usuario.xhtml um campo inputHidden armazenando a senhaCriptografada:
<h:inputHidden value=”#{usuarioBean.senhaCriptografada}”/>
A classe UsuarioBean (métodos editar e salvar) ficará conforme o exemplo a seguir:
public String editar() {
this.senhaCriptografada = this.usuario.getSenha();
return "/publico/usuario";
}
public String salvar() {
FacesContext context = FacesContext.getCurrentInstance();
String senha = this.usuario.getSenha();
if (senha != null &&
senha.trim().length() > 0 &&
!senha.equals(this.confirmarSenha)) {
FacesMessage facesMessage = new FacesMessage("A senha não foi confirmada corretamente");
context.addMessage(null, facesMessage);
return null;
}
if (senha != null && senha.trim().length() == 0) {
this.usuario.setSenha(this.senhaCriptografada);
} else {
String senhaCripto = DigestUtils.md5DigestAsHex(senha.getBytes());
this.usuario.setSenha(senhaCripto);
}
UsuarioRN usuarioRN = new UsuarioRN();
usuarioRN.salvar(this.usuario);
[...]
}