Java验证码(图片、字符串)生成工具

深碍√TFBOYSˉ_ 2024-04-06 13:07 126阅读 0赞

验证码生成工具

本工具可以生成:

  • 数字+字符
  • 纯数字
  • 纯字符

验证码样式:

  • 字符串
  • base64 字符图片验证码

主要方法:

  • generateCaptchaImage:获取图片验证码
  • generateCaptchaImageVerifyCode:获取图片验证码:(更多自定义参数)
  • getVerifyCode:获取验证码
  • getStringVerifyCode:获取默认长度验证码
  • getNumberVerifyCode:获取默认长度纯数字验证码
  • getStringAndNumberVerifyCode:获取默认长度,数字加字符

    @Slf4j
    public class VerifyCodeUtils {

    1. // 加载字体并缓存
    2. private static volatile Font cacheFont = null;
    3. //默认宽度
    4. private static final Integer imgWidth = 150;
    5. //默认高度
    6. private static final Integer imgHeight = 50;
    7. //随机字符串和数字
    8. private static final String STRING_NUMBER_VERIFY_CODE = "123456789ABCDEFGHJKLNPQRSTUVXYZ";
    9. //随机字符串
    10. private static final String STRING_VERIFY_CODE = "ABCDEFGHIJKLMNPOQRSTUVWXYZ";
    11. //随机数
    12. private static Random random = new Random();
    13. //默认验证码长度
    14. private static int DEFAULT_VERIFY_CODE_LENGTH = 4;
    15. //验证码最大长度
    16. private static int MAX_VERIFY_CODE_LENGTH = 6;
    17. //默认干扰线系数
    18. private static float DEFAULT_INTERFERE_RATE = 0.1f;
    19. //默认噪声系数
    20. private static float DEFAULT_NOISE_RATE = 0.3f;
  1. /**
  2. * 纯数字验证码
  3. * @param length 4-6位,默认4位
  4. * @return
  5. */
  6. private static String generateNumberVerifyCode(int length) {
  7. String code;
  8. if (length == 6){
  9. code = String.valueOf(ThreadLocalRandom.current().nextInt(0, 999999));
  10. }else if (length == 5){
  11. code = String.valueOf(ThreadLocalRandom.current().nextInt(0, 99999));
  12. }else {
  13. code = String.valueOf(ThreadLocalRandom.current().nextInt(0, 9999));
  14. }
  15. if (code.length() == length) {
  16. return code;
  17. } else {
  18. StringBuilder str = new StringBuilder();
  19. for(int i = 0; i < length - code.length(); ++i) {
  20. str.append("0");
  21. }
  22. return str + code;
  23. }
  24. }
  25. /**
  26. * 生成随机验证码
  27. * @param length
  28. * @param sources
  29. * @return
  30. */
  31. private static String generateVerifyCode(int length, String sources) {
  32. if (sources == null || sources.length() == 0) {
  33. sources = STRING_NUMBER_VERIFY_CODE;
  34. }
  35. int codeLen = sources.length();
  36. Random rand = new Random();
  37. StringBuilder verifyCode = new StringBuilder(length);
  38. for (int i = 0; i < length; i++) {
  39. verifyCode.append(sources.charAt(rand.nextInt(codeLen - 1)));
  40. }
  41. return verifyCode.toString();
  42. }
  43. /**
  44. * 默认长度4,数字加字符串
  45. * @return
  46. */
  47. public static String getStringAndNumberVerifyCode() {
  48. return generateVerifyCode(DEFAULT_VERIFY_CODE_LENGTH, STRING_NUMBER_VERIFY_CODE);
  49. }
  50. /**
  51. * 默认长度4,获取纯数字验证码
  52. * @return
  53. */
  54. public static String getNumberVerifyCode() {
  55. return generateNumberVerifyCode(DEFAULT_VERIFY_CODE_LENGTH);
  56. }
  57. /**
  58. * 默认长度4,字符串
  59. * @return
  60. */
  61. public static String getStringVerifyCode() {
  62. return generateVerifyCode(DEFAULT_VERIFY_CODE_LENGTH, STRING_VERIFY_CODE);
  63. }
  64. /**
  65. * 获取验证码
  66. * @param length 验证码长度 最长不超过6位 ,默认4位
  67. * @param type 验证码类型 0-数字+字符,1-纯数字,2-纯字符 默认0
  68. * @return
  69. */
  70. public static String getVerifyCode(int length, int type) {
  71. if (length == 0){
  72. length = DEFAULT_VERIFY_CODE_LENGTH;
  73. }
  74. if (length < DEFAULT_VERIFY_CODE_LENGTH){
  75. throw new DescribeException("length不能小于"+DEFAULT_VERIFY_CODE_LENGTH, ServerCodeEnum.PARAM_ERROR.getCode());
  76. }
  77. if (length > MAX_VERIFY_CODE_LENGTH){
  78. throw new DescribeException("length不能大于"+MAX_VERIFY_CODE_LENGTH,ServerCodeEnum.PARAM_ERROR.getCode());
  79. }
  80. if (type == 1){
  81. return generateNumberVerifyCode(length);
  82. }
  83. if (type == 2){
  84. return generateVerifyCode(length, STRING_VERIFY_CODE);
  85. }
  86. return generateVerifyCode(length, STRING_NUMBER_VERIFY_CODE);
  87. }
  88. /**
  89. * 返回base64图片验证码
  90. * @param code 验证码
  91. * @param imgWidth 宽度
  92. * @param imgHeight 高度
  93. * @return
  94. */
  95. public static String generateCaptchaImageVerifyCode(String code, Integer imgWidth, Integer imgHeight){
  96. if (imgWidth == null){
  97. imgWidth = VerifyCodeUtils.imgWidth;
  98. }
  99. if (imgHeight == null){
  100. imgHeight = VerifyCodeUtils.imgHeight;
  101. }
  102. return generateCaptchaImage(imgWidth, imgHeight, code,DEFAULT_INTERFERE_RATE , DEFAULT_NOISE_RATE);
  103. }
  104. /**
  105. * 返回base64图片验证码
  106. * @param code 验证码
  107. * @param imgWidth 宽度
  108. * @param imgHeight 高度
  109. * @param interfereRate 干扰性系数0.0-1.0
  110. * @param noiseRate 噪声系数 0.0-1.0
  111. * @return
  112. */
  113. public static String generateCaptchaImage(String code, int imgWidth, int imgHeight,float interfereRate, float noiseRate){
  114. return generateCaptchaImage(imgWidth, imgHeight, code, interfereRate, noiseRate);
  115. }
  116. /**
  117. * 生成验证码图片
  118. * @param imgWidth 宽度
  119. * @param imgHeight 高度
  120. * @param code 验证码
  121. * @param interfereRate 干扰系数 0.1-1
  122. * @param noiseRate 噪声系数 0.1-1
  123. */
  124. private static String generateCaptchaImage(int imgWidth, int imgHeight, String code, float interfereRate, float noiseRate){
  125. String base64Img = null;
  126. try {
  127. int length = code.length();
  128. BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
  129. Random rand = new Random();
  130. Graphics2D graphics2D = image.createGraphics();
  131. graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  132. // 设置边框色
  133. graphics2D.setColor(Color.GRAY);
  134. graphics2D.fillRect(0, 0, imgWidth, imgHeight);
  135. // 设置背景色
  136. Color c = new Color(235, 235, 235);
  137. graphics2D.setColor(c);
  138. graphics2D.fillRect(0, 1, imgWidth, imgHeight - 2);
  139. //绘制干扰
  140. interfere(image, graphics2D, imgWidth, imgHeight, interfereRate, noiseRate);
  141. //使图片扭曲
  142. shear(graphics2D, imgWidth, imgHeight, c);
  143. //字体颜色
  144. graphics2D.setColor(getRandColor(30, 100));
  145. //字体大小
  146. int fontSize = imgHeight >= 50 ? imgHeight - 8 : imgHeight >= 30 ? imgHeight - 6 : imgHeight - 4;
  147. //字体 Fixedsys/Algerian
  148. Font font = new Font("Fixedsys", Font.ITALIC, fontSize);
  149. graphics2D.setFont(font);
  150. char[] chars = code.toCharArray();
  151. for (int i = 0; i < length; i++) {
  152. //设置旋转
  153. AffineTransform affine = new AffineTransform();
  154. //控制在-0.3-0.3
  155. double theta = ((Math.PI / 4 * rand.nextDouble())*0.3) * (rand.nextBoolean() ? 1 : -1);
  156. affine.setToRotation(theta, (imgWidth / length) * i + fontSize / 2, imgHeight / 2);
  157. graphics2D.setTransform(affine);
  158. int x = ((imgWidth - 10) / length) * i + 4;
  159. //文字居中
  160. FontMetrics fm = graphics2D.getFontMetrics();
  161. int stringAscent = fm.getAscent();
  162. int stringDescent = fm.getDescent ();
  163. int y = imgHeight / 2 + (stringAscent - stringDescent) / 2;
  164. graphics2D.drawChars(chars, i, 1, x, y);
  165. }
  166. graphics2D.dispose();
  167. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  168. ImageIO.write(image, "jpg", outputStream);
  169. Base64.Encoder encoder = Base64.getEncoder();
  170. base64Img = encoder.encodeToString(outputStream.toByteArray());
  171. base64Img = base64Img.replaceAll("\n", "").replaceAll("\r", "");
  172. }catch (Exception e){
  173. log.error("生成图片验证码异常:", e);
  174. }
  175. return base64Img;
  176. }
  177. /**
  178. * 绘制干扰线和噪点
  179. * @param image 图像
  180. * @param graphics2D 二维图
  181. * @param imgWidth 宽度
  182. * @param imgHeight 高度
  183. * @param interfereRate 干扰线率 0.1-1
  184. * @param noiseRate 噪声率 0.1-1
  185. */
  186. private static void interfere(BufferedImage image,Graphics2D graphics2D, int imgWidth, int imgHeight, float interfereRate, float noiseRate){
  187. Random random = new Random();
  188. graphics2D.setColor(getRandColor(160, 200));
  189. int line = (int) (interfereRate * 100);
  190. for (int i = 0; i < line; i++) {
  191. int x = random.nextInt(imgWidth - 1);
  192. int y = random.nextInt(imgHeight - 1);
  193. int xl = random.nextInt(6) + 1;
  194. int yl = random.nextInt(12) + 1;
  195. graphics2D.drawLine(x, y, x + xl + 40, y + yl + 20);
  196. }
  197. int area = (int) (noiseRate * imgWidth * imgHeight)/10;
  198. for (int i = 0; i < area; i++) {
  199. int x = random.nextInt(imgWidth);
  200. int y = random.nextInt(imgHeight);
  201. int rgb = getRandomIntColor();
  202. image.setRGB(x, y, rgb);
  203. }
  204. }
  205. /**
  206. * 获取随机颜色
  207. * @param fc
  208. * @param bc
  209. * @return
  210. */
  211. private static Color getRandColor(int fc, int bc) {
  212. if (fc > 255) {
  213. fc = 255;
  214. }
  215. if (bc > 255) {
  216. bc = 255;
  217. }
  218. int r = fc + random.nextInt(bc - fc);
  219. int g = fc + random.nextInt(bc - fc);
  220. int b = fc + random.nextInt(bc - fc);
  221. return new Color(r, g, b);
  222. }
  223. /**
  224. * 获取rgb
  225. * @return
  226. */
  227. private static int getRandomIntColor() {
  228. int[] rgb = getRandomRgb();
  229. int color = 0;
  230. for (int c : rgb) {
  231. color = color << 8;
  232. color = color | c;
  233. }
  234. return color;
  235. }
  236. /**
  237. * 获取随机rgb颜色
  238. * @return
  239. */
  240. private static int[] getRandomRgb() {
  241. int[] rgb = new int[3];
  242. for (int i = 0; i < 3; i++) {
  243. rgb[i] = random.nextInt(255);
  244. }
  245. return rgb;
  246. }
  247. /**
  248. * 使图片扭曲
  249. * @param g
  250. * @param w1
  251. * @param h1
  252. * @param color
  253. */
  254. private static void shear(Graphics g, int w1, int h1, Color color) {
  255. distortionX(g, w1, h1, color);
  256. distortionY(g, w1, h1, color);
  257. }
  258. /**
  259. * 使图片x轴扭曲
  260. * @param g
  261. * @param w1
  262. * @param h1
  263. * @param color
  264. */
  265. private static void distortionX(Graphics g, int w1, int h1, Color color) {
  266. int period = random.nextInt(4);
  267. boolean borderGap = true;
  268. int frames = 2;
  269. int phase = random.nextInt(4);
  270. for (int i = 0; i < h1; i++) {
  271. double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
  272. g.copyArea(0, i, w1, 1, (int) d, 0);
  273. if (borderGap) {
  274. g.setColor(color);
  275. g.drawLine((int) d, i, 0, i);
  276. g.drawLine((int) d + w1, i, w1, i);
  277. }
  278. }
  279. }
  280. /**
  281. * 使图片y轴扭曲
  282. * @param g
  283. * @param w1
  284. * @param h1
  285. * @param color
  286. */
  287. private static void distortionY(Graphics g, int w1, int h1, Color color) {
  288. int period = random.nextInt(20) + 10;
  289. boolean borderGap = true;
  290. int frames = 20;
  291. int phase = 8;
  292. for (int i = 0; i < w1; i++) {
  293. double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
  294. g.copyArea(i, 0, 1, h1, 0, (int) d);
  295. if (borderGap) {
  296. g.setColor(color);
  297. g.drawLine(i, (int) d, i, 0);
  298. g.drawLine(i, (int) d + h1, i, h1);
  299. }
  300. }
  301. }
  302. /**
  303. * 创建字体
  304. * @param style 字体样式
  305. * @param size 字体大小
  306. * @return 字体
  307. */
  308. private static Font createFont(int style, int size) {
  309. if (cacheFont != null) {
  310. return cacheFont.deriveFont(style, size);
  311. } else {
  312. cacheFont = loadFont();
  313. return cacheFont;
  314. }
  315. }
  316. /**
  317. * 加载字体,解决Centos 7.2 以后,使用OpenJDK11 生成不了验证码问题
  318. * @return 生成验证码的字体
  319. */
  320. private static Font loadFont() {
  321. if (StringUtils.startsWithIgnoreCase(System.getProperty("os.name"), "windows")) {
  322. // windows 操作系统直接返回即可
  323. return new Font("Fixedsys", Font.BOLD, 0);
  324. } else {
  325. // linux 操作系统字体需要特殊处理
  326. try {
  327. InputStream inputStream = null;
  328. Resource fontFile = new DefaultResourceLoader().getResource("classpath:ALGER.TTF");
  329. try {
  330. // 加载字体配置文件
  331. Resource fontProperties = new DefaultResourceLoader().getResource("classpath:fontconfig.properties");
  332. System.setProperty("sun.awt.fontconfig", fontProperties.getURL().getPath());
  333. inputStream = fontFile.getInputStream();
  334. return Font.createFont(Font.TRUETYPE_FONT, inputStream);
  335. } catch (Exception e) {
  336. log.error("Create Font Error", e);
  337. } finally {
  338. IOUtils.closeQuietly(inputStream);
  339. }
  340. } catch (Exception e) {
  341. log.error("Load Font Error", e);
  342. }
  343. return null;
  344. }
  345. }
  346. /**
  347. * 生成验证码
  348. * @param code
  349. * @param imgWidth
  350. * @param imgHeight
  351. * @return
  352. */
  353. private static String generateCaptchaImage(String code, Integer imgWidth, Integer imgHeight) {
  354. if (StringUtils.isBlank(code)) {
  355. return null;
  356. }
  357. imgWidth = imgWidth == null ? VerifyCodeUtils.imgWidth : imgWidth;
  358. imgHeight = imgHeight == null ? VerifyCodeUtils.imgHeight: imgHeight;
  359. Integer codeLength = code.length();
  360. BufferedImage bufferedImg = null;
  361. String base64Img = null;
  362. try {
  363. int x = 0;
  364. // 每个字符的宽度
  365. x = imgWidth / (codeLength + 1);
  366. int fontHeight = 0;
  367. // 字体的高度
  368. fontHeight = imgHeight - 10;
  369. float codeY;
  370. codeY = (float) (imgHeight - 10.1);
  371. // 生成一张图片
  372. bufferedImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
  373. Graphics2D graph = bufferedImg.createGraphics();
  374. // 填充图片颜色
  375. graph.setColor(Color.WHITE);
  376. graph.fillRect(0, 0, imgWidth, imgHeight);
  377. // 创建字体
  378. Font font = new Font("Fixedsys", Font.TRUETYPE_FONT, fontHeight);
  379. graph.setFont(font);
  380. Random random = new Random();
  381. // 随机画干扰的圈圈
  382. int red = 0;
  383. int green = 0;
  384. int blue = 0;
  385. for (int i = 0; i < 10; i++) {
  386. int xs = random.nextInt(imgWidth);
  387. int ys = random.nextInt(imgHeight);
  388. int xe = random.nextInt(imgWidth / 16);
  389. int ye = random.nextInt(imgHeight / 16);
  390. red = random.nextInt(255);
  391. green = random.nextInt(255);
  392. blue = random.nextInt(255);
  393. graph.setColor(new Color(red, green, blue));
  394. graph.drawOval(xs, ys, xe, ye);
  395. }
  396. // 画随机线
  397. for (int i = 0; i < 20; i++) {
  398. int xs = random.nextInt(imgWidth);
  399. int ys = random.nextInt(imgHeight);
  400. int xe = xs + random.nextInt(imgWidth / 16);
  401. int ye = ys + random.nextInt(imgHeight / 16);
  402. red = random.nextInt(255);
  403. green = random.nextInt(255);
  404. blue = random.nextInt(255);
  405. graph.setColor(new Color(red, green, blue));
  406. graph.drawLine(xs, ys, xe, ye);
  407. }
  408. // 产生随机的颜色值,让输出的每个字符的颜色值都将不同。
  409. for (int i = 0; i < codeLength; i++) {
  410. red = random.nextInt(225);
  411. green = random.nextInt(225);
  412. blue = random.nextInt(225);
  413. graph.setColor(new Color(red, green, blue));
  414. String c = String.valueOf(code.charAt(i));
  415. graph.drawString(c, (float) (i + 0.5) * x, codeY);
  416. }
  417. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  418. ImageIO.write(bufferedImg, "jpg", outputStream);
  419. Base64.Encoder encoder = Base64.getEncoder();
  420. base64Img = encoder.encodeToString(outputStream.toByteArray());
  421. base64Img = base64Img.replaceAll("\n", "").replaceAll("\r", "");
  422. } catch (Exception e) {
  423. log.error("生成验证码异常:", e);
  424. }
  425. // return "data:image/jpg;base64," + base64Img;
  426. return base64Img;
  427. }
  428. }

发表评论

表情:
评论列表 (有 0 条评论,126人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java生成图片验证

    功能介绍:自定义图片尺寸和字符长度,随机背景颜色和字符颜色,随机字符偏移角度,字符平滑边缘,干扰线,噪点,背景扭曲。 VerifyCodeUtils类,生成图片流,然后不同框