JetCracker

Life-time learner's blog

[Java Web] [QapTcha] How to add captcha to JSF page

If you want to have prevent spam in your website, you probably need to use captcha.

CAPTCHA (play /ˈkæp.tʃə/) is a type of challenge-response test used in computing as an attempt to ensure that the response is generated by a human being. The process usually involves a computer asking a user to complete a simple test which the computer is able to grade. (Wikipedia)

Early CAPTCHAs such as these, generated by the...

You can see the examples of captchas on the pictures. Captcha is intended to protect the web site from spam, and it helps to avoid “unwanted” registrations, block robots. But it can also deter some “proper” users. As for me, I hate recognizing captchas!

What we need is a simple and user-friendly mechanism to detect robot. Fortunately there are plenty of different solutions for captchas on the internet!

Take a look at QapTcha – a simple, easy-to-use and intuitive captcha system. It needs human action instead of to read a hard text and it is a very lightweight jQuery plugin.

QapTcha Demo Screenshot

All you need is to drag the button from one side to another, and that makes it perfect for mobile devices.

Here I will show you an example of how we can embed this captcha into JSF web-application.

Using QapTcha in JSF

In order to understand how we can use the captch in JSF web-site, we have to look int how it (the QapTcha) works.

It is a simple draggable html component, which relies on jQuery and jQuery.UI javascript library. The captcha generates a random character sequence (a “qaptcha key”) and adds a hidden input-element (which name is equals to the qaptcha key) to the form on the web page. When we complete the captcha (drag it from one side to another), it sends an ajax-request to the server with the following POST parameters:

  • captcha_key = ‘generated qaptcha key’
  • action = ‘qaptcha’

So, if we take special actions, our server will know the generated key and we will be able to check if the captcha was disabled or not..

To embed the QapTcha in our web-application we need to do the wolowing:

  • create a servlet for processing QapTcha requests
  • add all necessary javascript and styles to the web-page
  • write some code in managed bean for validation of captcha

QapTcha Servlet

package in.xfiles.web.servlets;

import in.xfiles.core.helpers.StringUtils;
import in.xfiles.web.utils.SessionHelper;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * This servlet is intended to process requests from QapTcha.
 * It updates "qaptcha_key" parameter of session.
 *
 * @author danon
 */
@WebServlet(name = "QapTchaServlet", urlPatterns = {"/qaptcha"})
public class QapTchaServlet extends HttpServlet {

    /**
     * Processes requests for both HTTP
     * <code>GET</code> and
     * <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        final PrintWriter out = response.getWriter();
        try {
            if(request != null) {
                HttpSession session = request.getSession(true);
                SessionHelper.setSessionAttribute(session, "qaptcha_key", null);
                if(StringUtils.getValidString(request.getParameter("action")).equals("qaptcha")) {
                    SessionHelper.setSessionAttribute(request.getSession(true), "qaptcha_key", request.getParameter("qaptcha_key"));
                    out.print("{\"error\":false}");
                    return;
                }
            }
            out.print("{\"error\":true}");
        } finally {
            out.close();
        }

    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP
     * <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Servlet for QapTcha";
    }// </editor-fold>
}

This servlet checks if there are valid ‘action’ and ‘qaptcha_key’ parameters. If it is so, the server returns ‘{“error”:false}’, otherwise – ‘{“error”:true}’ (json format). The generated qaptcha key is stored as session attribute ‘qaptcha_key’, so we can use it in the future.

Javascript

You should add jQury and jQuery UI, and jQuery UI Touch scripts to the web page because QapTcha jquery-plugin is relies on it. Then, add QapTcha.jquery.min.js and corresponding CSS. And finally, to make it all work, add the following javascript:

      $(document).ready(function(){
        $('.QapTcha').QapTcha(
            {disabledSubmit:false,
             autoRevert:true,
             autoSubmit:false,
             PHPfile:'qaptcha',
             txtLock:'You can\'t submit. Please unlock.',
             txtUnlock:''
            });
    });

No, to add captcha, you only have to add <div class="QapTcha"/> in your form.

Validation in managed bean

In my case, I used captcha in password recovery form. The code of managed bean follows.

package in.xfiles.web.users;

import in.xfiles.core.ejb.PasswordManagerLocal;
import in.xfiles.core.ejb.UserManagerLocal;
import in.xfiles.core.entity.User;
import in.xfiles.core.helpers.StringUtils;
import in.xfiles.web.utils.JSFHelper;
import java.io.Serializable;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
import org.apache.log4j.Logger;

/**
 * View scoped managed bean for password recovery
 *
 * @author danon
 */
@ManagedBean
@ViewScoped
public class PasswordRecoveryBean implements Serializable {

    private final Logger log = Logger.getLogger(RegistrationBean.class);

    @EJB
    private UserManagerLocal um;

    @EJB
    private PasswordManagerLocal pm;

    private String login;
    private String recoveryStatus;

    /**
     * Creates a new instance of LoginBean
     */
    public PasswordRecoveryBean() {
    }

    private boolean validateUserInput() {
        if(StringUtils.isEmpty(login))
            return false;
        // TODO: validate login (regexp)
        return true;
    }

    private boolean tryRecover(String login) {
       User u = um.getUserByLogin(login);
       if(u == null)
           return false;
       return pm.recoverPassword(u.getUserId());
    }

    public void recoverAction(ActionEvent evt) {
        String qaptchaKey = JSFHelper.getSessionAttribute(String.class, "qaptcha_key");
        if(qaptchaKey == null) {
            JSFHelper.addMessage(FacesMessage.SEVERITY_ERROR, "Validation:", "Captcha validation failed.");
            recoveryStatus = "fail";
            return;
        }
        if(!JSFHelper.getRequest().getParameterMap().containsKey(qaptchaKey)) {
            JSFHelper.addMessage(FacesMessage.SEVERITY_ERROR, "Validation:", "Captcha validation failed");
            recoveryStatus = "fail";
            return;
        }

        if(!validateUserInput()) {
            JSFHelper.addMessage(FacesMessage.SEVERITY_ERROR, "Validation:", "Check your input and try again.");
            recoveryStatus = "fail";
            return;
        }

        if(!tryRecover(login)) {
            JSFHelper.addMessage(FacesMessage.SEVERITY_ERROR, "Error:", "Password cannot be recovered.");
            recoveryStatus = "fail";
            return;
        }

        JSFHelper.addMessage(FacesMessage.SEVERITY_INFO, "Information:", "New password has been sent to your e-mail.");
        recoveryStatus = "ok";
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        if(login == null)
            this.login = null;
        else
            this.login = login.trim();
    }

    public String getRecoveryStatus() {
        return recoveryStatus;
    }

    public void setRecoveryStatus(String recoveryStatus) {
        this.recoveryStatus = recoveryStatus;
    }
}

Here is a screenshot of what I have.

Example of QapTcha: password recovery form screenshot

Example of QapTcha: password recovery form screenshot

I hope this article is useful. Good luck and have fun!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: