Java实现一个复杂的图形验证码

  • Post author:
  • Post category:java


这个验证码将包括以下特性:

1. 7个字符,包含英文大小写字母、数字和符号;

2. 不同的字符将有不同的颜色;

3. 字符随机旋转;

4. 随机噪点(包括随机噪点颜色、数量和大小);

5. 随机干扰弧线(包括干扰弧线颜色、数量、宽度和角度);

6. 随机干扰直线(包括干扰直线颜色、数量、宽度和长度);

7. 随机背景色;

8. 随机字体(包括字体、大小和样式)。

下面是这个验证码的示例代码:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
 public class ComplexCaptchaUtil {
    private static final int IMAGE_WIDTH = 200; // 验证码图片宽度
    private static final int IMAGE_HEIGHT = 80; // 验证码图片高度
    private static final int CHAR_COUNT = 7; // 字符个数
    private static final String CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+[];:/?<>,.'{}\"\\|~`"; // 字符集
    private static final int CHAR_SPACING = 20; // 字符间距
    private static final int ROTATION_RANGE = 45; // 字符随机旋转角度范围
    private static final int NOISE_POINT_COUNT = 200; // 随机噪点数量
    private static final int NOISE_POINT_RADIUS_MIN = 1; // 随机噪点半径最小值
    private static final int NOISE_POINT_RADIUS_MAX = 3; // 随机噪点半径最大值
    private static final int NOISE_ARC_COUNT = 6; // 随机弧线数量
    private static final int NOISE_ARC_WIDTH_MIN = 5; // 随机弧线宽度最小值
    private static final int NOISE_ARC_WIDTH_MAX = 10; // 随机弧线宽度最大值
    private static final int NOISE_ARC_ANGLE_MIN = -30; // 随机弧线角度最小值
    private static final int NOISE_ARC_ANGLE_MAX = 30; // 随机弧线角度最大值
    private static final int NOISE_LINE_COUNT = 20; // 随机直线数量
    private static final int NOISE_LINE_WIDTH_MIN = 1; // 随机直线宽度最小值
    private static final int NOISE_LINE_WIDTH_MAX = 3; // 随机直线宽度最大值
    private static final int NOISE_LINE_LENGTH_MIN = 10; // 随机直线长度最小值
    private static final int NOISE_LINE_LENGTH_MAX = 30; // 随机直线长度最大值
    private static final String[] FONT_NAMES = { "Arial", "Courier New", "Georgia", "Times New Roman", "Verdana" }; // 字体名称
    private static final int FONT_SIZE_MIN = 36; // 字体大小最小值
    private static final int FONT_SIZE_MAX = 48; // 字体大小最大值
    private static final int FONT_STYLE_NORMAL = Font.PLAIN; // 字体样式-正常
    private static final int FONT_STYLE_BOLD = Font.BOLD; // 字体样式-加粗
    private static final int FONT_STYLE_ITALIC = Font.ITALIC; // 字体样式-斜体
    private static final int FONT_STYLE_BOLD_ITALIC = Font.BOLD | Font.ITALIC; // 字体样式-加粗斜体
    private static final int BACKGROUND_COLOR_RANGE = 100; // 背景色范围
     public static void generate(OutputStream os) throws Exception {
        // 创建验证码图片
        BufferedImage image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
         // 获取绘制对象
        Graphics2D g = image.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
         // 生成随机背景色
        Random random = new Random();
        int r = random.nextInt(BACKGROUND_COLOR_RANGE);
        int gVal = random.nextInt(BACKGROUND_COLOR_RANGE);
        int b = random.nextInt(BACKGROUND_COLOR_RANGE);
        g.setColor(new Color(r, gVal, b));
        g.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
         // 设置字体
        Font font = new Font(FONT_NAMES[random.nextInt(FONT_NAMES.length)],
                getRandomFontStyle(), getRandomFontSize());
        g.setFont(font);
         // 随机生成字符、颜色、位置和旋转角度
        char[] captcha = new char[CHAR_COUNT];
        Color[] colors = new Color[CHAR_COUNT];
        int[] xPos = new int[CHAR_COUNT];
        int[] yPos = new int[CHAR_COUNT];
        double[] rotations = new double[CHAR_COUNT];
        for (int i = 0; i < CHAR_COUNT; i++) {
            captcha[i] = CHAR_SET.charAt(random.nextInt(CHAR_SET.length()));
            colors[i] = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
            xPos[i] = CHAR_SPACING * i + 10;
            yPos[i] = IMAGE_HEIGHT / 2 + font.getSize() / 2 - random.nextInt(10);
            rotations[i] = Math.toRadians(random.nextInt(ROTATION_RANGE) - ROTATION_RANGE / 2);
        }
         // 绘制字符
        for (int i = 0; i < CHAR_COUNT; i++) {
            g.setColor(colors[i]);
            AffineTransform transform = new AffineTransform();
            transform.rotate(rotations[i], xPos[i], yPos[i]);
            g.setTransform(transform);
            g.drawString(String.valueOf(captcha[i]), xPos[i], yPos[i]);
        }
         // 随机生成噪点
        for (int i = 0; i < NOISE_POINT_COUNT; i++) {
            int x = random.nextInt(IMAGE_WIDTH);
            int y = random.nextInt(IMAGE_HEIGHT);
            int radius = random.nextInt(NOISE_POINT_RADIUS_MAX - NOISE_POINT_RADIUS_MIN + 1)
                    + NOISE_POINT_RADIUS_MIN;
            Color color = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
            g.setColor(color);
            g.fillOval(x, y, radius, radius);
        }
         // 随机生成干扰弧线
        for (int i = 0; i < NOISE_ARC_COUNT; i++) {
            int x1 = random.nextInt(IMAGE_WIDTH);
            int y1 = random.nextInt(IMAGE_HEIGHT);
            int x2 = random.nextInt(IMAGE_WIDTH);
            int y2 = random.nextInt(IMAGE_HEIGHT);
            int width = random.nextInt(NOISE_ARC_WIDTH_MAX - NOISE_ARC_WIDTH_MIN + 1) + NOISE_ARC_WIDTH_MIN;
            int angle1 = random.nextInt(NOISE_ARC_ANGLE_MAX - NOISE_ARC_ANGLE_MIN + 1) + NOISE_ARC_ANGLE_MIN;
            int angle2 = random.nextInt(NOISE_ARC_ANGLE_MAX - NOISE_ARC_ANGLE_MIN + 1) + NOISE_ARC_ANGLE_MIN;
            Color color = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
            g.setColor(color);
            g.setStroke(new BasicStroke(width));
            g.draw(new QuadCurve2D.Float(x1, y1, IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2 + 20, x2, y2));
            g.setStroke(new BasicStroke(1));
        }
         // 随机生成干扰直线
        for (int i = 0; i < NOISE_LINE_COUNT; i++) {
            int x1 = random.nextInt(IMAGE_WIDTH);
            int y1 = random.nextInt(IMAGE_HEIGHT);
            int x2 = x1
                    + (int) ((random.nextInt(NOISE_LINE_LENGTH_MAX - NOISE_LINE_LENGTH_MIN + 1)
                            + NOISE_LINE_LENGTH_MIN)
                            * Math.cos(Math.toRadians(random.nextInt(360))));
            int y2 = y1
                    + (int) ((random.nextInt(NOISE_LINE_LENGTH_MAX - NOISE_LINE_LENGTH_MIN + 1)
                            + NOISE_LINE_LENGTH_MIN)
                            * Math.sin(Math.toRadians(random.nextInt(360))));
            int width = random.nextInt(NOISE_LINE_WIDTH_MAX - NOISE_LINE_WIDTH_MIN + 1) + NOISE_LINE_WIDTH_MIN;
            Color color = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
            g.setColor(color);
            g.setStroke(new BasicStroke(width));
            g.drawLine(x1, y1, x2, y2);
            g.setStroke(new BasicStroke(1));
        }
         // 将验证码图片输出到流中
        ImageIO.write(image, "JPEG", os);
    }
     private static int getRandomFontSize() {
        return new Random().nextInt(FONT_SIZE_MAX - FONT_SIZE_MIN + 1) + FONT_SIZE_MIN;
    }
     private static int getRandomFontStyle() {
        int[] styles = { FONT_STYLE_NORMAL, FONT_STYLE_BOLD, FONT_STYLE_ITALIC, FONT_STYLE_BOLD_ITALIC };
        return styles[new Random().nextInt(styles.length)];
    }
}

这个示例代码生成一个包含7个数字与字母字符的验证码图片,使用了随机颜色的干扰线来增加验证码的难度。可以调整常量值以自定义验证码的属性。

可以通过调用CaptchaUtil.generate(os)方法传入输出流来得到一个新的验证码图片。

    @RequestMapping("/test1")
    public void test1(HttpServletResponse response) throws IOException {

        ServletOutputStream responseOutputStream = response.getOutputStream();
        try {
            ComplexCaptchaUtil.generate(responseOutputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        responseOutputStream.flush();
        responseOutputStream.close();
    }



版权声明:本文为qq_39827935原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。