cherry.png

Creating simple captcha

07.11.2017
checkmark3

Java

Small Java code snippet to create the image containing captcha and example of adding it to a code of the site or blog.
This is how a working version of our captcha looks like:
captcha
reload.png
First, determine the algorithm. At the entrance we'll give a random combination of letters, then we'll rotate each letter on 35 degrees clockwise or counterclockwise alternately, and at the end we'll collect the resulting images into one line.
Why 35 degrees? If you take the angle more, then it will be hard for yourself to read this captcha. For example lettersNandZwill be similar like each other. If you take the angle less than 35 degrees, then it will be easy to read this captcha byOCRprograms.
Text string with the value of the captcha we'll store in the session attibute. For those who are not familiar withJavaI'll say that the attributes of the session and the session itself inJavaare stored in the application server and not available to user's browser. So any hiddenIDunder the captcha in our case, are simply not necessary. When user submit a form to the server, we'll compare the saved value of the captcha, with what the user entered, and then we'll decide whether to handle his request or not.
So, we go to the algorithm:
1. To start, we need a collection of any characters, for example, the capital English letters. For my blog I chose this variant:
letter_a.pngletter_b.pngletter_c.pngletter_d.pngletter_e.pngletter_f.pngletter_g.pngletter_h.pngletter_i.pngletter_j.pngletter_k.pngletter_l.pngletter_m.pngletter_n.pngletter_o.pngletter_p.pngletter_q.pngletter_r.pngletter_s.pngletter_t.pngletter_u.pngletter_v.pngletter_w.pngletter_x.pngletter_y.pngletter_z.png
But for the best protection against spam, it is recommended to expand this collection by adding to it lowercase letters and numbers. Images must be inGIForPNGformat with transparency, because such images complicate the process of machine recognition of text - not allOCRprograms work with such images.
2. Creating a servlet, which we'll call later:

Java

@WebServlet(name = "Captcha", urlPatterns = {"/captcha"})
public class Captcha extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doResponse(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doResponse(request, response);
    }

    protected void doResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //TODO
    }
}
Next, we'll gradually filldoResponsemethod and supplement it by additional methods.
3. Getting a random combination of letters:

Java

private static char[] getRandomStringImpl() {

    char[] randomString = new char[8];

    Random random = new Random();

    int capitalLetter;

    for (int i = 0; i < 8; i++) {
        // capital English letters in the ASCII
        // symbol table starts with code 65
        capitalLetter = 65 + random.nextInt(26);
        randomString[i] = (char) capitalLetter;
    }

    return randomString;
}
4. Calling this method fromdoResponsemethod and writing result into session:

Java

char[] captcha = getRandomStringImpl();
request.getSession().setAttribute("captcha", String.valueOf(captcha));
Thus we got the value of the captcha, which we'll compare later with what the user enters into the form, unless of course, he is a user and not a robot.
5. Now we collecting an array of pictures with letters and rotating each of them at a 35 degree:

Java

BufferedImage[] images = new BufferedImage[8];

for (int i = 0; i < captcha.length; i++) {
    images[i] = ImageIO.read(Captcha.class.getResourceAsStream("/captcha/" + captcha[i] + ".png"));
    if (i % 2 == 0) {
        images[i] = rotateImage(images[i], 35);
    } else {
        images[i] = rotateImage(images[i], -35);
    }
}
6. Creating a method of turning the picture by a specified angle:

Java

private BufferedImage rotateImage(BufferedImage buffImage, double angle) {

    double radian = Math.toRadians(angle);
    double sin = Math.abs(Math.sin(radian));
    double cos = Math.abs(Math.cos(radian));

    int width = buffImage.getWidth();
    int height = buffImage.getHeight();

    int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
    int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);

    BufferedImage rotatedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);

    Graphics2D graphics = rotatedImage.createGraphics();

    graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
    graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
    graphics.drawImage(buffImage, 0, 0,null);
    graphics.dispose();

    return rotatedImage;
}
7. Now combining the obtained images into a single string. In order to the letters were a bit covered by each other, shifting each subsequent letter to the left on 40% of the width of the previous letter:

Java

int imageSize = 30;
// calculating the hypotenuse by Pythagorean theorem
// the maximum image size obtains by rotation on 45 degrees
int rotatedImageSize = (int) Math.sqrt(imageSize * imageSize * 2);

BufferedImage captcha_img = new BufferedImage(rotatedImageSize * 7 / 10 * 6 + rotatedImageSize, rotatedImageSize, BufferedImage.TYPE_INT_ARGB);

for (int i = 0; i < captcha.length; i++) {
    captcha_img.getGraphics().drawImage(images[i],rotatedImageSize * i / 10 * 6, 0, null);
}
8. Last step - returning to the user the image containing captcha:

Java

ImageIO.write(captcha_img, "png", response.getOutputStream());
Everything is ready, it remains only to collect all the code into asingle unit
Well, a working example of this code posted on this site in the comments and on theContactspage

facebookvkontaktetwitterodnoklassnikimailrulivejournal

Comments

O0O0O0O0
Commentator
01.01.1970 03:00 (MSK)
There are no comments yet.. You can be the first..