Como adicionar Captcha na aplicação JSF – solução com Recaptcha
Um problema muito sério em aplicações que possuem acesso aberto na internet é o uso de softwares automatizados que enviam inúmeras requisições seguidas, normalmente com a pretensão de gerar spam, diminuindo o desempenho de sistemas e gerando prejuízo às empresas.
A técnica mais utilizada para evitar esse problema é o emprego de imagens com letras distorcidas, que em alguns casos pode ter a adição de uma seqüência obscurecida das letras ou dos dígitos que aparecem na tela. O objetivo é evitar o uso de robôs, já que eles não conseguem fazer a leitura de imagens. Essa solução é conhecida como Captcha (da sigla Completely Automated Public Turing test to tell Computers and Humans Apart) e embora tenha recebido esse nome, é visto como um Teste de Turing reverso, uma vez que é administrado pelo computador e não por humanos.
No meus testes em JSF consegui utilizar as soluções do Tomahawk, do PrimeFaces, do JCaptcha e do Recaptcha. Todos são muitos simples de adicionar à aplicação, mas o Tomahawk e o JCaptcha integrado ao JSF usam, para fazer a validação, um atributo em escopo de sessão com o valor da chave, o que muitas vezes não é desejado a nível de arquitetura. Já o PrimeFaces tem como base o Recaptcha da Google, mas a maioria dos desenvolvedores (eu me incluo entre eles) usa o RichFaces como solução de biblioteca de interface rica, tornando-se impensável colocar o PrimeFaces na aplicação apenas para ter o componente Captcha.
Por fim, meu último teste foi o Recaptcha, da Google. Vamos às vantagens da solução:
- Não utiliza a chave como atributo de sessão para validar a imagem;
- Ajuda a digitalizar livros e jornais (no momento estão sendo digitalizadas edições do The New York Times e livros do Google Books);
- É acessível para deficientes visuais;
- É popular: empresas como Facebook e TicketMaster utilizam;
- Tem compatibilidade com várias linguagens e aplicações.
Obviamente que nem tudo são flores. As desvantagens são:
- O serviço da Google está na web, portanto se ele cair (pouco provável), ou se o host onde a aplicação Java está não conseguir comunicação com ele, o componente não será exibido;
- O componente possui uma visual mais complexo do que os outros, o que às vezes confunde o usuário;
Pesando as desvantagens e as vantagens, decidimos optar pelo Recaptcha, que se mostrou com a melhor relação entre os dois aspectos. Vamos ao que interessa: como colocar na aplicação.
A Solução
- Cadastre-se no site do Recaptcha para receber as chaves pública e privada que serão utilizadas pelo componente. O Google obrigado o uso de um domínio para o emprego do captcha. O cadastro pode ser feito aqui;
- Coloque o jar do Recaptcha no classpath da sua aplicação. O download pode ser feito aqui;
- Em um componente h:outputText coloque o código HTML gerado dinamicamente pelo serviço da Google com o atributo escape=false. O value do componente deve apontar para um método do Managed Bean que gera o captcha, porque a cada requisição deve ser lançado um novo desafio;
- Implemente o método getCodigoHtmlRecaptcha(), conforme abaixo;
- Faça agora a validação do texto na Action chamada pelo commandButton ou commandLink, conforme métodos abaixo:
/**
* Gera o código HTML gerado pelo componente da Google.
*
* @return String - código HTML dinâmico
*/
public String getCodigoHtmlRecaptcha() {
ReCaptcha c = ReCaptchaFactory.newReCaptcha("chave pública fornecida pela Google", "chave privada fornecida pela Google", false);
return c.createRecaptchaHtml(null, null);
}
public String processaImagem() {
try {
if (!validaTextoImagem()) {
/**
* COLOQUE AQUI A LÓGICA PARA A RESPOSTA ESTAR EM BRANCO OU INVÁLIDA.
*/
return null; // permanece na mesma tela
}
} catch (ValidacaoCaptchaException e) {
/**
* COLOQUE AQUI A LÓGICA PARA O COMPONENTE NÃO TER SIDO EXIBIDO CORRETAMENTE.
*/
e.printStackTrace();
return null; // permanece na mesma tela
}
// COLOQUE AQUI A LÓGICA PARA A IMAGEM TER SIDO VALIDADA COM SUCESSO
return "sucessoCaptcha"; // o texto foi validado corretamente
}
/**
* Valida o texto da imagem digitado pelo usuário.
*
* @return boolean indicando se o texto foi validado ou não
* @throws ValidacaoCaptchaException caso o componente não tenha sido exibido corretamente
*/
private boolean validaTextoImagem() throws ValidacaoCaptchaException {
HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String enderecoRemoto = req.getRemoteAddr();
ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
reCaptcha.setPrivateKey("chave privada fornecida pela Google");
String textoCriptografado = req.getParameter("recaptcha_challenge_field");
String resposta = req.getParameter("recaptcha_response_field");
/**
* Testa se o campo desafio está nulo ou vazio, ou se o campo resposta veio nulo (vem String
* vazia se o usuário não preencheu) e lança exception.
*/
if (textoCriptografado == null || textoCriptografado.equals("") || resposta == null) {
throw new ValidacaoCaptchaException("Dados submetidos não recuperados. Talvez exista um problema na exibição do componente captcha.");
}
ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(enderecoRemoto, textoCriptografado, resposta);
if (resposta.isEmpty() || !reCaptchaResponse.isValid()) {
return false; // resposta em branco ou inválida
} else {
return true; // texto válido
}
}
Pronto! Somente isso. Quero ressaltar, antes que perguntem nos comentários, que ValidacaoCaptchaException é uma simples checked exception que extende Exception.
Seu componente será exibido na página conforme imagem abaixo:
Obviamente que a solução proposta nesse artigo é a mais simples possível. O ideal é não colocar a lógica de geração do código HTML em um método get, como foi feito. Para facilitar a manutenção, a chave privada pode ser colocada no web.xml, já que ela é referenciada mais de uma vez no código. Outro detalhe é que o uso de um componente facelet ajudaria na reutilização do captcha em várias páginas e ajudaria na validação do texto informado no componente e na estruturação do código como um todo. Só que mostrarei essa abordagem, juntamente com as outras duas melhorias comentadas acima em um próximo post.
Modificando o Idioma e a Cor
O Recaptcha da Google permite que você escolha o idioma das mensagens que aparecem quando se coloca o mouse em cima dos botões do componente e da frase que pede para inserir o texto. Outro atributo que você pode modificar é o esquema de cores do componente. Os dois atributos podem ser modificados com o seguinte código javascript:
<script type="text/javascript">
var RecaptchaOptions = {
theme : 'cor_escolhida', // as opções são 'red' (default), 'white', 'blackglass' e 'clean'
lang : 'pt'
};
</script>
Caso você queira modificar apenas um dos atributos (o tema ou o idioma), coloque apenas a linha correspondente, sem a vírgula que separa as duas instruções. O resultado com o tema branco (white) e o idioma português pode ser visto abaixo:


Muito legal, outra alteranativa é usar o primfeaces
http://www.primefaces.org/showcase/ui/captcha.jsf
abraçow