All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.emily.infrastructure.captcha.CaptchaUtils Maven / Gradle / Ivy

There is a newer version: 5.0.1
Show newest version
package com.emily.infrastructure.captcha;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Random;

/**
 * 图形验证码
 * 获取字体名称
 * 
 *     {@code
 *      String[] names = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
 *     }
 * 
* 获取字体对象 *
{@code
 * Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
 * }
* * @author Emily */ public class CaptchaUtils { private static final Random RANDOM = new Random(); /** * 数字 */ private static final String[] DIGIT = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; /** * 字母 */ private static final String[] LETTER = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; /** * 字母数字 */ private static final String[] ALPHANUMERIC = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; /** * 图片格式 */ public static final String FORMAT_NAME = "JPEG"; /** * @param width 画板宽度 * @param height 画板高度 * @param count 验证码个数 * @param fontSize 字体大小 * @return 绘制数字图形验证码,不带干扰线 * @throws IOException 异常 */ public static Captcha createDigit(int width, int height, int count, int fontSize) throws IOException { return createDigit(width, height, count, fontSize, false, null); } /** * @param width 画板宽度 * @param height 画板高度 * @param count 验证码个数 * @param fontSize 字体大小 * @param line 是否画干扰线 * @param lineCount 干扰线数量 * @return 绘制数字图形验证码 * @throws IOException 异常 */ public static Captcha createDigit(int width, int height, int count, int fontSize, boolean line, Integer lineCount) throws IOException { /** * 字体名又分为两大类:中文字体名:宋体,楷体,黑体等; *           英文字体名:Arial,Times New Roman等; */ Font font = new Font("Times New Roman", Font.ITALIC, fontSize); // 绘制的字符串 String[] code = getDrawCode(CaptchaType.DIGIT, count); return create2D(width, height, code, font, line, lineCount); } /** * @param width 画板宽度 * @param height 画板高度 * @param count 验证码个数 * @param fontSize 字体大小 * @return 图形验证码对象 * @throws IOException 绘制字母图形验证码 */ public static Captcha createLetter(int width, int height, int count, int fontSize) throws IOException { return createLetter(width, height, count, fontSize, false, null); } /** * @param width 画板宽度 * @param height 画板高度 * @param count 验证码个数 * @param fontSize 字体大小 * @param line 是否画干扰线 * @param lineCount 干扰线数量 * @return 图形验证码对象 * @throws IOException 绘制字母图形验证码 */ public static Captcha createLetter(int width, int height, int count, int fontSize, boolean line, Integer lineCount) throws IOException { /** * 字体名又分为两大类:中文字体名:宋体,楷体,黑体等; *           英文字体名:Arial,Times New Roman等; */ Font font = new Font("Times New Roman", Font.ITALIC, fontSize); // 绘制的字符串 String[] code = getDrawCode(CaptchaType.LETTER, count); return create2D(width, height, code, font, line, lineCount); } /** * @param width 画板宽度 * @param height 画板高度 * @param count 验证码个数 * @param fontSize 字体大小 * @return 图形验证码对象 * @throws IOException 绘制数字字母图形验证码 */ public static Captcha createAlphanumeric(int width, int height, int count, int fontSize) throws IOException { return createAlphanumeric(width, height, count, fontSize, false, null); } /** * 绘制数字字母图形验证码 * * @param width 画板宽度 * @param height 画板高度 * @param count 验证码个数 * @param fontSize 字体大小 * @param line 是否画干扰线 * @param lineCount 干扰线数量 * @return 图形验证码对象 * @throws IOException 异常 */ public static Captcha createAlphanumeric(int width, int height, int count, int fontSize, boolean line, Integer lineCount) throws IOException { /** * 字体名又分为两大类:中文字体名:宋体,楷体,黑体等; *           英文字体名:Arial,Times New Roman等; */ Font font = new Font("Times New Roman", Font.ITALIC, fontSize); // 绘制的字符串 String[] code = getDrawCode(CaptchaType.ALPHANUMERIC, count); return create2D(width, height, code, font, line, lineCount); } /** * 绘制图形验证码 * * @param width 画板宽度 * @param height 画板高度 * @param code 验证码 * @param font 字体 * @param line 是否绘制干扰线 * @param lineCount 干扰线条数 * @return 生成的图形验证码对象 * @throws IOException 生成图形验证码 */ public static Captcha create2D(int width, int height, String[] code, Font font, boolean line, Integer lineCount) throws IOException { // 创建空白图片 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 获取图片画笔 Graphics graphic = image.getGraphics(); // 绘制矩形背景(实心矩形) graphic.fillRect(0, 0, width, height); /** * Font.PLAIN(普通) * Font.BOLD(加粗) * Font.ITALIC(斜体) * Font.BOLD+ Font.ITALIC(粗斜体) */ graphic.setFont(font); // 画图形验证码 drawCode(graphic, width, height, code); // 画干扰线 if (line) { drawLine(graphic, width, height, lineCount); } ByteArrayOutputStream bas = new ByteArrayOutputStream(); ImageIO.write(image, FORMAT_NAME, bas); return Captcha.newBuilder().withCode(String.join("", code)).withImage(bas.toByteArray()).build(); } /** * @param graphic 画板对象 * @param width 画板宽度 * @param height 画板高度 * @param code 图形验证码字符串 * 画图形验证码 */ protected static void drawCode(Graphics graphic, int width, int height, String[] code) { // 计算文字长度,计算居中的x点坐标,即字符串左边位置 FontMetrics fm = graphic.getFontMetrics(graphic.getFont()); // 字符串宽度 int textWidth = fm.stringWidth(String.join("", code)); // 字符串左侧x坐标 int x = (width - textWidth) / 2; /** * * 文本高度=lead+ascent+descent * ************************************************************** * lead 字体前导|行间距 * ************************************************************** * * * ascent 字体升序 *

* * *************************************************************** baseline *

* descent 字体降序 * **************************************************************** * * 上图就是四线格,文本占用的有效高度就是ascent+descent * 居中计算规则就是:(画板的高度height-文本的有效高度(ascent+descent))/2获得文本非占用空间的一半,由于绘制字符串是以基线为准,所以再加上ascent后绘制字符串以基线为准在一条线上并且居中 * */ int y = (height - (fm.getAscent() + fm.getDescent())) / 2 + fm.getAscent(); for (int i = 0; i < code.length; i++) { // 设置随机颜色 graphic.setColor(new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255))); // 绘制字符串 graphic.drawString(code[i], x + (textWidth / code.length) * i, y); } } /** * 获取绘制的验证码 * * @param type 字符串类型 * @param count 字符个数 * @return 字符串数组 */ protected static String[] getDrawCode(CaptchaType type, int count) { String[] code = new String[count]; Random ran = new Random(); for (int i = 0; i < count; i++) { switch (type) { case DIGIT -> code[i] = DIGIT[ran.nextInt(DIGIT.length)]; case LETTER -> code[i] = LETTER[ran.nextInt(LETTER.length)]; default -> code[i] = ALPHANUMERIC[ran.nextInt(ALPHANUMERIC.length)]; } } return code; } /** * @param graphic 画板对象 * @param width 画板宽度 * @param height 画板高度 * @param lineCount 干扰线条数 * 绘制干扰线 */ protected static void drawLine(Graphics graphic, int width, int height, Integer lineCount) { // 画干扰线 for (int i = 0; i < lineCount; i++) { // 设置随机颜色 graphic.setColor(new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255))); // 随机画线 graphic.drawLine(RANDOM.nextInt(width), RANDOM.nextInt(height), RANDOM.nextInt(width), RANDOM.nextInt(height)); } } /** * 将图片字节数组转换成base64字符串 * * @param bytes 图片字节数组 * @param format 图片格式,如png,jpg,gif,jpeg等 * @return 图片base64字符串 */ public static String convertStreamToBase64(byte[] bytes, String format) { return String.format("data:image/image/%s;base64,%s", format, new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy