package servlets; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.security.SecureRandom; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import datastructures.ContrastCaptcha; /** * Servlet implementation class CaptchaServlet */ public class CaptchaServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public CaptchaServlet() { super(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(true); /* * Create CAPTCHA. */ BufferedImage bImage; ContrastCaptcha captchaSolution; { final int MIN_CAPTCHA_LENGTH = 6; final int CAPTCHA_LENGTH_VARIABILITY = 0; // final int MIN_SPACES = 0; // final int MAX_SPACES = 0; final int FONT_SIZE = 20; final int CHARACTER_WIDTH = 13; final int CHARACTER_HEIGHT = 14; final int LEFT_PADDING = CHARACTER_WIDTH; final int RIGHT_PADDING = LEFT_PADDING; final int CAPTCHA_TOP_PADDING = CHARACTER_HEIGHT; final int CAPTCHA_BOTTOM_PADDING = CAPTCHA_TOP_PADDING; final int CAPTCHA_WIDTH = LEFT_PADDING + CHARACTER_WIDTH * (MIN_CAPTCHA_LENGTH + CAPTCHA_LENGTH_VARIABILITY) + RIGHT_PADDING; final int CAPTCHA_HEIGHT = CHARACTER_HEIGHT + CAPTCHA_TOP_PADDING + CAPTCHA_BOTTOM_PADDING; final int MIN_CHAR_HEIGHT = CHARACTER_HEIGHT; final int MAX_CHAR_HEIGHT = CAPTCHA_HEIGHT - CHARACTER_HEIGHT - 4; final int MIN_NUM_LINES = 3; final int NUM_LINES_VARIABILITY = 1; final int LINES_MAX_X = CAPTCHA_WIDTH; final int LINES_MAX_Y = CAPTCHA_HEIGHT; /* * Create Graphics2D object. */ bImage = new BufferedImage(CAPTCHA_WIDTH, CAPTCHA_HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bImage.createGraphics(); /* * Draw the image. */ final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; Random rand = new SecureRandom(); char captchaChars[] = new char[MIN_CAPTCHA_LENGTH + rand.nextInt(CAPTCHA_LENGTH_VARIABILITY + 1)]; for (int i = 0; i < captchaChars.length; ++i) { captchaChars[i] = ALPHABET.charAt(rand.nextInt(ALPHABET.length())); } /* * We want to add spaces, but they can't be the first or last character. * NOTE: We don't actually want spaces because it messes up the colours. */ /* * { * int spacesIndices[] = new int[captchaChars.length - 2]; * for(int i = 0; i < spacesIndices.length; ++i){ * spacesIndices[i] = i + 1; * } * //Partial Fisher-Yates shuffle. * int num_spaces = rand.nextInt(MAX_SPACES - MIN_SPACES + 1) + MIN_SPACES; * for(int i = 0; i < num_spaces; ++i){ * int j = rand.nextInt(spacesIndices.length - i); * * //Swap i and j. * int tmp = spacesIndices[i]; * spacesIndices[i] = spacesIndices[j]; * spacesIndices[j] = tmp; * } * for(int i = 0; i < num_spaces; ++i){ * captchaChars[spacesIndices[i]] = ' '; * } * } */ // We want to add spaces, but they can't be the first or last character. Color charColours[] = new Color[captchaChars.length]; ContrastCaptcha.CaptchaColours charColoursForExport[] = new ContrastCaptcha.CaptchaColours[captchaChars.length];// For checking the colours of the CAPTCHA. { // Initialise array. for (int i = 0; i < charColours.length; ++i) { charColours[i] = Color.BLACK; charColoursForExport[i] = ContrastCaptcha.CaptchaColours.UNIMPORTANT; } // Choose one char that's 0.1%, 10%, and 40% charColours[1] = new Color(0.85f, 0.85f, 0.85f, 1f); charColoursForExport[1] = ContrastCaptcha.CaptchaColours.LIGHTEST_GREY; charColours[2] = new Color(0.8f, 0.8f, 0.8f, 1f); charColoursForExport[2] = ContrastCaptcha.CaptchaColours.LIGHT_GREY; charColours[3] = new Color(0.6f, 0.6f, 0.6f, 1f); // charColoursForExport[3] = CaptchaColours.DARK_GREY; for (int i = charColours.length - 2; i > 1; --i) { int j = rand.nextInt(i + 1) + 1; // Swap i and j. { Color tmp = charColours[i]; charColours[i] = charColours[j]; charColours[j] = tmp; } { ContrastCaptcha.CaptchaColours tmp = charColoursForExport[i]; charColoursForExport[i] = charColoursForExport[j]; charColoursForExport[j] = tmp; } } } captchaSolution = new ContrastCaptcha(captchaChars, charColoursForExport); g2d.setBackground(Color.WHITE); g2d.clearRect(0, 0, CAPTCHA_WIDTH, CAPTCHA_HEIGHT); g2d.setColor(Color.black); g2d.setFont(new Font("Liberation Mono", Font.PLAIN, FONT_SIZE)); /* * Draw the characters. */ int x_position = LEFT_PADDING + (((MIN_CAPTCHA_LENGTH + CAPTCHA_LENGTH_VARIABILITY) - captchaChars.length) / 2) * CHARACTER_WIDTH; for (int i = 0; i < captchaChars.length; ++i) { g2d.setColor(charColours[i]); int y_position = rand.nextInt(MAX_CHAR_HEIGHT + 1) + MIN_CHAR_HEIGHT; g2d.drawString(Character.toString(captchaChars[i]), x_position, y_position); x_position += CHARACTER_WIDTH; } /* * Draw lines. */ int num_lines = rand.nextInt(NUM_LINES_VARIABILITY + 1) + MIN_NUM_LINES; for (int i = 0; i < num_lines; ++i) { int x1 = rand.nextInt(LINES_MAX_X + 1); int y1 = rand.nextInt(LINES_MAX_Y + 1); int x2 = rand.nextInt(LINES_MAX_X + 1); int y2 = rand.nextInt(LINES_MAX_Y + 1); g2d.drawLine(x1, y1, x2, y2); } } session.setMaxInactiveInterval(6000);// Session invalidates after 60 minutes. session.setAttribute("captcha", captchaSolution); // Prevent from caching response.setHeader("Cache-Control", "private, no-store, no-cache, must-revalidate"); response.setHeader("Pragma", "no-cache"); // Return image response.setContentType("image/png"); ImageIO.write(bImage, "png", response.getOutputStream()); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }