Implementando Captcha com Servlets

Aprenda neste artigo a utilizar o Captcha e saiba como evitar spam em lista de discussões, chats, e muitos outros.

Por | @oficinadanet Programação
CAPTCHA (completely automated public turing test to tell computers and humans apart) é uma técnica utilizada para verificar se quem está requisitando um determinado serviço do software é um usuário, e não uma aplicação robô. a idéia basicamente é apresentar caracteres de validação para que sejam digitados pelo usuário para serem validados pela aplicação, esses caracteres devem ser legíveis a humanos, mas não para aplicações. geralmente isso é feito através de imagens com caracteres distorcidos.

A técnica é muito utilizada para evitar spam em listas de discussão, chats, e telas de cadastro que não requerem login.

Existem disponíveis algumas api’s para implementação de CAPTCHA em java, uma delas é a jCAPTCHA, que usaremos nesse artigo.

Como exemplo, tomaremos uma rela de comentário de um blog, colocaremos CAPTCHA para evitar que sejam postadas mensagens por rotinas automatizadas, gerando spam na lista de comentários.

Estrutura
Para iniciar a aplicação criaremos a estrutura básica de uma aplicação web, crie na pasta webapps do tomcat de acordo com a estrutura mostrada na figura 01.

Implementando Captcha com Servlets
figura 01. estrutura de diretórios.

Faça o download da api jCAPTCHA no seguinte endereço: target="_blank">http://prdownloads.sourceforge.net/jCAPTCHA/jCAPTCHA-bin-1.0-rc3.zip?download
Descompacte o arquivo .zip, pegue o arquivo jCAPTCHA-all-1.0-rc3.jar e coloque na pasta lib da aplicação.

Pojo
criaremos também uma classe comentario que será o pojo do nosso cadastro. como não é o objetivo desse artigo, não iremos persistir esses objetos, mas logicamente em uma aplicação real você teria que fazer isso.

package br.com.javamagazine.jairelton2;

public class comentario {
      private int id;
      private string nome;
      private string email;
      private string texto;

      public comentario(){
            nome = "";
            email = "";
            texto = "";      
      }

      public string getemail() {
            return email;
      }
      public void setemail(string email) {
            this.email = email;
      }
      public int getid() {
            return id;
      }
      public void setid(int id) {
            this.id = id;
      }
      public string getnome() {
            return nome;
      }
      public void setnome(string nome) {
            this.nome = nome;
      }
      public string gettexto() {
            return texto;
      }
      public void settexto(string texto) {
            this.texto = texto;
      }
}


Classe de criação e validação dos caracteres, precisaremos também de uma classe para gerenciar a criação das imagens e a validação dos caracteres, chamaremos a classe de CAPTCHA:

package br.com.javamagazine.jairelton2;
import java.awt.color;
import com.octo.CAPTCHA.component.image.backgroundgenerator.backgroundgenerator;
import com.octo.CAPTCHA.component.image.backgroundgenerator.funkybackgroundgenerator;
import com.octo.CAPTCHA.component.image.fontgenerator.fontgenerator;
import com.octo.CAPTCHA.component.image.fontgenerator.twistedandshearedrandomfontgenerator;
import com.octo.CAPTCHA.component.image.textpaster.randomtextpaster;
import com.octo.CAPTCHA.component.image.textpaster.textpaster;
import com.octo.CAPTCHA.component.image.wordtoimage.composedwordtoimage;
import com.octo.CAPTCHA.component.image.wordtoimage.wordtoimage;
import com.octo.CAPTCHA.component.word.wordgenerator.randomwordgenerator;
import com.octo.CAPTCHA.engine.image.listimageCAPTCHAengine;
import com.octo.CAPTCHA.image.gimpy.gimpyfactory;
import com.octo.CAPTCHA.service.image.defaultmanageableimageCAPTCHAservice;
import com.octo.CAPTCHA.service.image.imageCAPTCHAservice;

public class CAPTCHA {
      private imageCAPTCHAservice service;

      private static CAPTCHA instance = new CAPTCHA();
    
      /*a classe deve ser um singleton*/
      private CAPTCHA(){
            defaultmanageableimageCAPTCHAservice serv = new defaultmanageableimageCAPTCHAservice();
            serv.setCAPTCHAengine(new enginenumeros());
          
            service = serv;
      }

      public static CAPTCHA getinstance(){
            return instance;
      }
    
      public imageCAPTCHAservice getservice(){
            return service;
      }

      /*especializando um engine para gerar apenas números*/
      class enginenumeros extends listimageCAPTCHAengine {
            protected void buildinitialfactories() {
                     /*cria um um textpaster, o tamanho mínimo é de 5 caracteres
                      * maximo de 8, e a cor do texto será branca
                      */
                     textpaster textpaster =
                                new randomtextpaster(5, 8, color.white);
                    /*um gerador de background, a imagem terá 100 x 50 pixels
                     */
                     backgroundgenerator backgroundgenerator =
                                         new funkybackgroundgenerator(100, 50);
                     /* um gerador de fonte, é responsável por distorcer o
                      * texto, o tamanho mínimo da fonte é 25 e o maximo 30
      */
                     fontgenerator fontgenerator =
                              new twistedandshearedrandomfontgenerator(25, 30);
                     /* o objeto responsável por juntar o background, a fonte e
                      * o texto para gerar a imagem
                      */
                     wordtoimage wordtoimage =
                                     new composedwordtoimage(fontgenerator,
                                     backgroundgenerator, textpaster);
                     /* adiciona o factory randomwordgenerator recebe os
                      * caracteres válidos, no caso queremos apenas números
          */
                     this.addfactory(new gimpyfactory(new randomwordgenerator
                                                ("0123456789"), wordtoimage));
            }
       }  
}

Servlet de geração da imagem.

Temos que criar também um servlet para a geração da imagem, veja o código abaixo:
package br.com.javamagazine.jairelton2;
import java.awt.image.bufferedimage;
import java.io.bytearrayoutputstream;
import java.io.ioexception;
import javax.servlet.servletexception;
import javax.servlet.servletoutputstream;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import com.sun.image.codec.jpeg.jpegcodec;
import com.sun.image.codec.jpeg.jpegimageencoder;
public class CAPTCHAservlet extends javax.servlet.http.httpservlet{
      protected void doget(httpservletrequest request, httpservletresponse
                           response) throws servletexception, ioexception {
            servletoutputstream out = response.getoutputstream();
            bytearrayoutputstream jpegstream = new bytearrayoutputstream();
            try {
                  /*a imagem será gerada com base no id da sessão*/
                  string jsid = request.getsession().getid();
                  /*gera a imagem*/
                  bufferedimage challenge = CAPTCHA.getinstance().
                                    getservice().getimagechallengeforid(jsid);
                  /*codifica a imagem no formato jpeg*/
                  jpegimageencoder jpegencoder = jpegcodec.
                                                createjpegencoder(jpegstream);
                  jpegencoder.encode(challenge);
            } catch (exception e) {
               response.senderror(httpservletresponse.sc_internal_server_error);
               return;
            }
            /*transforma a imagem em um array de bytes*/
            byte[] jpegbytes = jpegstream.tobytearray();
            /*modifica os cabeçalhos http
* para que a imagem não seja armazenada em cache
*/
            response.setheader("cache-control", "no-store");

           response.setheader("pragma", "no-cache");
            response.setdateheader("expires", 0);
            response.setcontenttype("image/jpeg");
            /*envia a imagem para o cliente*/
                  out.write(jpegbytes);
                  out.flush();
                  out.close();
      }    
    
      protected void dopost(httpservletrequest request, httpservletresponse
                            response) throws servletexception, ioexception {
            doget(request, response);
      }              
}

Servlet de cadastro

Criaremos também um servlet para onde serão enviados os dados do formulário de comentários, esse servlet validará os caracteres digitados pelo usuário, permitindo ou não o cadastro do comentário de acordo com o resultado da validação:

package br.com.javamagazine.jairelton2;
import java.io.ioexception;
import javax.servlet.requestdispatcher;
import javax.servlet.servletexception;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;

public class comentarioservlet extends javax.servlet.http.httpservlet{
      protected void doget(httpservletrequest request, httpservletresponse
                           response) throws servletexception, ioexception {
            dopost(request, response);
      }    

      protected void dopost(httpservletrequest request, httpservletresponse
                            response) throws servletexception, ioexception {
        /*os caracteres serão validados de acordo com o id da sessão
         *pois eles foram gerados a partir dele
         */
  string jsid = request.getsession().getid();
  /*recupera os caracteres que foram digitados pelo usuário*/
  string CAPTCHA = request.getparameter("CAPTCHA");
  /*valida a entrada do usuario, retorna true se valido, false se

   invalido*/
  boolean CAPTCHAok = CAPTCHA.getinstance().
                      getservice().validateresponseforid(jsid, CAPTCHA);
        /*cria um comentário com base nos dados do formulário*/
  comentario comentario = new comentario();
        comentario.setnome(request.getparameter("nome"));
        comentario.setemail(request.getparameter("email"));
        comentario.settexto(request.getparameter("texto"));
    
  request.getsession().setattribute("comentario", comentario);

        if(CAPTCHAok){
         /*caso o usuário tenha digitado os caracteres corretamente, processa
         *normalmente, no nosso caso fazemos apenas um redirecionamento para
         *outro pagina, mas em uma aplicação real os dados seriam persistidos*/
      requestdispatcher dispatcher =
                     getservletcontext().getrequestdispatcher("/ok.jsp");
      dispatcher.forward(request, response);
  }else{  
   /*caso os caracteres sejam inválidos, volta para o formulário com uma
    *mensagem de erro*/
      request.setattribute("mensagem", "preencha os caracteres    
                                        corretamente!");      
      requestdispatcher dispatcher =
                   getservletcontext().getrequestdispatcher("/formulario.jsp");
      dispatcher.forward(request, response);      
  }
      }              
}

Configuração

Vejamos agora a configuração do arquivo web.xml, esse arquivo deve estar dentro da pasta web-inf da aplicação:
Implementando Captcha com Servlets

Veja que o servlet CAPTCHAservlet é mapeado para a url /CAPTCHA.jpg, isso é interessante para mascarar a implementação do CAPTCHA, parecendo ser apenas uma imagem simples.


Formulário
Como o servlet acima vai receber os dados do formulário, logicamente precisamos criar esse formulário, faremos com jsp, crie a página formulario.jsp na pasta da aplicação:

<%@ page language="java" contenttype="text/html; charset=iso-8859-1"
pageencoding="iso-8859-1"%>

Implementando Captcha com Servlets
Implementando Captcha com Servlets
Implementando Captcha com Servlets

note que no trecho Implementando Captcha com Servlets é inserida uma imagem na página apontando para CAPTCHA.jpg, isso não é uma imagem comum e sim o servlet CAPTCHAservlet que devolverá uma imagem normalmente, mascarando a implementação como dito anteriormente.

Página de confirmação
Por fim, criaremos uma segunda página jsp para exibir os dados cadastrados em caso de sucesso, em uma aplicação real, essa seria a página de confirmação do comentário. a página deve ser criada na pasta da aplicação com o nome de ok.jsp:

<%@ page language="java" contenttype="text/html; charset=iso-8859-1"
pageencoding="iso-8859-1"%>

Implementando Captcha com Servlets

Teste
Pronto, nossa implementação de CAPTCHA já está feita, inicie o servidor tomcat e acesse o endereço http://localhost:8080/javamagazine2/formulario.jsp , será exibida a página apresentada na figura 02.

Implementando Captcha com Servlets
figura 02. página da aplicação

Digitando os caracteres corretamente você será redirecionado para a página mostrando os dados do seu comentário, caso contrário será mostrada uma mensagem de erro.

Essa é uma forma simples de evitar transtornos com spam e acessos indevidos a páginas, no exemplo de comentários, mas há muitas outras aplicações. a api jCAPTCHA permite varias costumizações inclusive para implementação por som, para mais detalhes consulte a documentação no site http://www.jCAPTCHA.org.

Autor:Jair Elton Batista de Souza


========================================
Conheça nosso parceiro e fonte desta matéria:

Implementando Captcha com Servlets  

Mais sobre:
Share Tweet
Recomendado
Comentários
Carregar comentários
Destaquesver tudo