Monday, April 07, 2008

Create Your Own Simple, Fully Customized Captcha in Desktop Java


Captcha in webpages is designed to avoid bots, I used captcha in my desktop application to avoid accidental processing on high critical transaction as you can see at my screenshot above.

First I initialized array of alphanumeric, I don't use some of them like '1', 'i', '5', 's' to avoid misreading.


private String[] token = new String[]{"3", "r", "c", "t", "9",
"n", "x", "a", "j", "8",
"d", "p", "f", "y", "7",
"h", "k"};

This array will be shuffled everytime the class created using Collections.shuffle() method.

java.util.List l = Arrays.asList(token);
Collections.shuffle(l);

Then simply read the shuffled array into a String variable.

captcha = token[0] + token[1] + token[2] + token[3];

The next thing to do is paint the component. Step one is drawing the horizontal and vertical gridlines with random color.
Graphics2D g2d = (Graphics2D) g.create();
Random r = new Random();

for (int x = 10; x < getWidth(); x+=10) {
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawLine(x, 0, x, getHeight());
}

for (int y = 10; y < getHeight(); y+=10) {
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawLine(0, y, getWidth(), y);
}

Step two is drawing text shadow. This step is optional.
g2d.setColor(Color.BLACK);
fm = getFontMetrics(bigFont);
g2d.drawString(token[0], 7, fm.getHeight() + 2);
g2d.drawString(token[1], 7 + fm.getWidths()[0], fm.getHeight() + 2);
g2d.drawString(token[2], 7 + (fm.getWidths()[0]*2), fm.getHeight() + 2);
g2d.drawString(token[3], 7 + (fm.getWidths()[0]*3), fm.getHeight() + 2);

Step three is drawing the text itself, each letter is drawn using random color too.
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[0], 5, fm.getHeight());
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[1], 5 + fm.getWidths()[0], fm.getHeight());
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[2], 5 + (fm.getWidths()[0]*2), fm.getHeight());
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[3], 5 + (fm.getWidths()[0]*3), fm.getHeight());

After painting is done, the last thing should be done is create comparing method.
public boolean match(String input) {
return (input.equalsIgnoreCase(captcha));
}

From your application, just create a new instance of the class, waiting for user input then compare them.
wCaptcha c = new wCaptcha();
. . .
. . .
. . .
c.match(txtInput.getText());

The complete source is below.

import java.util.*;
import java.awt.*;

import javax.swing.*;

public class wCaptcha extends JPanel {
private String[] token = new String[]{"3", "r", "c", "t", "9",
"n", "x", "a", "j", "8",
"d", "p", "f", "y", "7",
"h", "k"};
private String captcha = new String();

private Font bigFont;
private FontMetrics fm;

public wCaptcha() {
super();

initData();
initUI();
}

private void initData() {
java.util.List l = Arrays.asList(token);
Collections.shuffle(l);

captcha = token[0] + token[1] + token[2] + token[3];
}

private void initUI() {
setDoubleBuffered(true);

bigFont = new Font("Monospaced",Font.BOLD,getSize2D() + 20);
fm = getFontMetrics(bigFont);

setBorder(null);
setPreferredSize(new Dimension((fm.getWidths()[0]*4) + 27,
fm.getHeight() + fm.getMaxDescent() + 2));
setMaximumSize(new Dimension((fm.getWidths()[0]*4) + 27,
fm.getHeight() + fm.getMaxDescent() + 2));
setMinimumSize(new Dimension((fm.getWidths()[0]*4) + 27,
fm.getHeight() + fm.getMaxDescent() + 2));
setBackground(Color.WHITE);
}

public void paintComponent(Graphics g) {
super.paintComponent(g);

Random r = new Random();

Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setFont(bigFont);
g2d.translate(0, 0);

for (int x = 10; x < getWidth(); x+=10) {
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawLine(x, 0, x, getHeight());
}

for (int y = 10; y < getHeight(); y+=10) {
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawLine(0, y, getWidth(), y);
}

g2d.setColor(Color.BLACK);
g2d.drawString(token[0], 7, fm.getHeight() + 2);
g2d.drawString(token[1], 7 + fm.getWidths()[0], fm.getHeight() + 2);
g2d.drawString(token[2], 7 + (fm.getWidths()[0]*2), fm.getHeight() + 2);
g2d.drawString(token[3], 7 + (fm.getWidths()[0]*3), fm.getHeight() + 2);

g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[0], 5, fm.getHeight());
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[1], 5 + fm.getWidths()[0], fm.getHeight());
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[2], 5 + (fm.getWidths()[0]*2), fm.getHeight());
g2d.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g2d.drawString(token[3], 5 + (fm.getWidths()[0]*3), fm.getHeight());
}

public boolean match(String input) {
return (input.equalsIgnoreCase(captcha));
}
}

3 comments:

Lakelandmom said...

The site in general is nice and useful, thanks. I commented on this post because the captcha caught my attention.

jodie marsh said...

Wow i needed captcha code so thanks for sharing i will use it for my blog. I will check it out if any problems i will conatct you.

Good website

Anonymous said...

Im having a little bit to trouble with builing a field which displays the text, can anyone help please?

border=0
Free Advertising