仓库源文站点原文


title: Java 生成验证码 Captcha date: 2022-03-19 10:27:25 categories: 学习&开发&实现 tag:


方法效率较低,推荐使用缓存,重复使用验证码

// 验证码宽度
private static final Integer CAPTCHA_WIDTH = 150;
// 验证码高度
private static final Integer CAPTCHA_HEIGHT = 40;
// 验证码最大旋转角度(不推荐修改)
private static final Integer CAPTCHA_CHAR_ROTATE = 5;
// 干扰线数量
private static final Integer CAPTCHA_LINE_NUM = 5;
// 验证码数量
private static final Integer CAPTCHA_CHAR_NUM = 4;
// 验证码的保存格式
private static final String CAPTCHA_CONTENT_TYPE = "PNG";

public static String createCaptcha(OutputStream outputStream) throws PortableException {
    BufferedImage image = new BufferedImage(CAPTCHA_WIDTH, CAPTCHA_HEIGHT, BufferedImage.TYPE_INT_BGR);
    Graphics2D g = (Graphics2D) image.getGraphics();
    g.fillRect(0, 0, CAPTCHA_WIDTH, CAPTCHA_HEIGHT);

    for (int i = 0; i < CAPTCHA_LINE_NUM; i++) {
        drawRandomLine(g);
    }
    for (int i = 0; i < CAPTCHA_LINE_NUM; i++) {
        drawLeftToRightLine(g);
    }
    StringBuilder ans = new StringBuilder();
    for (int i = 0; i < CAPTCHA_CHAR_NUM; i++) {
        ans.append(drawString(g, i + 1));
    }
    try {
        ImageIO.write(image, CAPTCHA_CONTENT_TYPE, outputStream);
    } catch (IOException e) {
        throw PortableException.of("B-01-002");
    }
    return ans.toString();
}

private static void drawRandomLine(Graphics2D g) {
    int xs = RANDOM.nextInt(CAPTCHA_WIDTH);
    int xe = RANDOM.nextInt(CAPTCHA_WIDTH);
    int ys = RANDOM.nextInt(CAPTCHA_HEIGHT);
    int ye = RANDOM.nextInt(CAPTCHA_HEIGHT);
    g.setFont(ARIAL_FONT);
    g.setColor(getRandomColor());
    g.drawLine(xs, ys, xe, ye);
}

private static void drawLeftToRightLine(Graphics2D g) {
    int xs = RANDOM.nextInt(CAPTCHA_WIDTH / 2);
    int xe = CAPTCHA_WIDTH / 2 + RANDOM.nextInt(CAPTCHA_WIDTH / 2);
    int ys = RANDOM.nextInt(CAPTCHA_HEIGHT / 2);
    int ye = CAPTCHA_HEIGHT / 2 + RANDOM.nextInt(CAPTCHA_HEIGHT / 2);
    g.setFont(ARIAL_FONT);
    g.setColor(getRandomColor());
    g.drawLine(xs, ys, xe, ye);
}

private static String drawString(Graphics2D g, Integer num) {
    // 保证左侧和右侧不要贴边,总共留出一个字符的空间
    int baseX = (int) (CAPTCHA_WIDTH * 1.0 / (ImageUtils.CAPTCHA_CHAR_NUM + 1) * num);
    AffineTransform old = g.getTransform();
    String c = getRandomChar();

    g.setFont(ARIAL_FONT);
    g.setColor(getRandomColor());
    g.rotate(Math.toRadians(RANDOM.nextInt(CAPTCHA_CHAR_ROTATE * 2) - CAPTCHA_CHAR_ROTATE));
    g.drawString(c, baseX, CAPTCHA_HEIGHT / 3 * 2);
    g.setTransform(old);

    return c;
}

private static Color getRandomColor() {
    int upper = 128;
    int lower = 0;
    int r = lower + RANDOM.nextInt(upper);
    int g = lower + RANDOM.nextInt(upper);
    int b = lower + RANDOM.nextInt(upper);
    return new Color(r, g, b);
}

private static String getRandomChar() {
    return String.valueOf(CAPTCHA_CHAR.charAt(RANDOM.nextInt(CAPTCHA_CHAR.length())));
}