验证码生成工具
1 /**
2 * <html>
3 * <body>
4 * <P> Copyright 1994 JsonInternational</p>
5 * <p> All rights reserved.</p>
6 * <p> Created on 19941115</p>
7 * <p> Created by Jason</p>
8 * </body>
9 * </html>
10 */
11 package cn.ucaner.alpaca.framework.utils.validate;
12
13 import java.awt.Color;
14 import java.awt.Font;
15 import java.awt.Graphics;
16 import java.awt.Graphics2D;
17 import java.awt.image.BufferedImage;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.util.Date;
22 import java.util.Random;
23
24 import javax.imageio.ImageIO;
25
26 /**
27 * @Package:cn.ucaner.framework.utils
28 * @ClassName:ValidateCode
29 * @Description: <p> [验证码生成工具.]可生成数字,大写,小写字母及三者混合类型的验证码.
30 * 支持自定义验证码字符数量; 支持自定义验证码图片的大小; 支持自定义需排除的特殊字符;
31 * 支持自定义干扰线的数量; 支持自定义验证码图文颜色</p>
32 * @Author: - Jason
33 * @CreatTime:2017年10月20日 下午4:04:15
34 * @Modify By:
35 * @ModifyTime:
36 * @Modify marker:
37 * @version V1.0
38 */
39 public class ValidateCode {
40
41 // 图片的宽度。
42 private int width = 160;
43 // 图片的高度。
44 private int height = 40;
45 // 验证码字符个数
46 private int codeCount = 5;
47 // 验证码干扰线数
48 private int lineCount = 150;
49 // 验证码
50 private String code = null;
51 // 验证码图片Buffer
52 private BufferedImage buffImg = null;
53
54 // 验证码范围,去掉0(数字)和O(拼音)容易混淆的(小写的1和L也可以去掉,大写不用了)
55 private char[] codeSequence = { '0',
56 // 'B',
57 // 'C',
58 // 'D',
59 // 'E',
60 // 'F',
61 // 'G',
62 // 'H',
63 // 'I',
64 // 'J',
65 // 'K',
66 // 'L',
67 // 'M',
68 // 'N',
69 // 'P',
70 // 'Q',
71 // 'R',
72 // 'S',
73 // 'T',
74 // 'U',
75 // 'V',
76 // 'W',
77 // 'X',
78 // 'Y',
79 // 'Z',
80 '1',
81 '2',
82 '3',
83 '4',
84 '5',
85 '6',
86 '7',
87 '8',
88 '9' };
89
90 /**
91 * @param width 图片宽
92 * @param height 图片高
93 */
94 public ValidateCode(int width, int height) {
95 this.width = width;
96 this.height = height;
97 this.createCode();
98 }
99
100 /**
101 * @param width 图片宽
102 * @param height 图片高
103 * @param codeCount 字符个数
104 * @param lineCount 干扰线条数
105 */
106 public ValidateCode(int width, int height, int codeCount, int lineCount) {
107 this.width = width;
108 this.height = height;
109 this.codeCount = codeCount;
110 this.lineCount = lineCount;
111 this.createCode();
112 }
113
114 public void createCode() {
115 int x = 0, fontHeight = 0, codeY = 0;
116 int red = 0, green = 0, blue = 0;
117
118 x = width / ( codeCount + 2 );//每个字符的宽度(左右各空出一个字符)
119 fontHeight = height - 2;//字体的高度
120 codeY = height - 4;
121
122 // 图像buffer
123 buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
124 Graphics2D g = buffImg.createGraphics();
125 // 生成随机数
126 Random random = new Random();
127 // 将图像填充为白色
128 g.setColor(Color.WHITE);
129 g.fillRect(0, 0, width, height);
130 // 创建字体,可以修改为其它的
131 Font font = new Font("Fixedsys", Font.PLAIN, fontHeight);
132 // Font font = new Font("Times New Roman", Font.ROMAN_BASELINE, fontHeight);
133 g.setFont(font);
134
135 for (int i = 0; i < lineCount; i++) {
136 // 设置随机开始和结束坐标
137 int xs = random.nextInt(width);//x坐标开始
138 int ys = random.nextInt(height);//y坐标开始
139 int xe = xs + random.nextInt(width / 8);//x坐标结束
140 int ye = ys + random.nextInt(height / 8);//y坐标结束
141
142 // 产生随机的颜色值,让输出的每个干扰线的颜色值都将不同。
143 red = random.nextInt(255);
144 green = random.nextInt(255);
145 blue = random.nextInt(255);
146 g.setColor(new Color(red, green, blue));
147 g.drawLine(xs, ys, xe, ye);
148 }
149
150 // randomCode记录随机产生的验证码
151 StringBuffer randomCode = new StringBuffer();
152 // 随机产生codeCount个字符的验证码。
153 for (int i = 0; i < codeCount; i++) {
154 String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]);
155 // 产生随机的颜色值,让输出的每个字符的颜色值都将不同。
156 red = random.nextInt(255);
157 green = random.nextInt(255);
158 blue = random.nextInt(255);
159 g.setColor(new Color(red, green, blue));
160 g.drawString(strRand, ( i + 1 ) * x, codeY);
161 // 将产生的四个随机数组合在一起。
162 randomCode.append(strRand);
163 }
164 // 将四位数字的验证码保存到Session中。
165 code = randomCode.toString();
166 }
167
168 public void write(String path) throws IOException {
169 OutputStream sos = new FileOutputStream(path);
170 this.write(sos);
171 }
172
173 public void write(OutputStream sos) throws IOException {
174 ImageIO.write(buffImg, "png", sos);
175 sos.close();
176 }
177
178 public BufferedImage getBuffImg() {
179 return buffImg;
180 }
181
182 public String getCode() {
183 return code;
184 }
185
186 /**
187 * 测试函数,默认生成到d盘
188 * @param args
189 */
190 public static void main(String[] args) {
191 ValidateCode vCode = new ValidateCode(160, 40, 4, 150);
192 try {
193 String path = "D:/" + new Date().getTime() + ".png";
194 System.out.println(vCode.getCode() + " >" + path);
195 vCode.write(path);
196 } catch (IOException e) {
197 e.printStackTrace();
198 }
199 }
200
201 /**
202 * 验证码类型为仅数字 0~9
203 */
204 public static final int TYPE_NUM_ONLY = 0;
205
206 /**
207 * 验证码类型为仅字母,即大写、小写字母混合
208 */
209 public static final int TYPE_LETTER_ONLY = 1;
210
211 /**
212 * 验证码类型为数字、大写字母、小写字母混合
213 */
214 public static final int TYPE_ALL_MIXED = 2;
215
216 /**
217 * 验证码类型为数字、大写字母混合
218 */
219 public static final int TYPE_NUM_UPPER = 3;
220
221 /**
222 * 验证码类型为数字、小写字母混合
223 */
224 public static final int TYPE_NUM_LOWER = 4;
225
226 /**
227 * 验证码类型为仅大写字母
228 */
229 public static final int TYPE_UPPER_ONLY = 5;
230
231 /**
232 * 验证码类型为仅小写字母
233 */
234 public static final int TYPE_LOWER_ONLY = 6;
235
236 /**
237 * 生成验证码字符串
238 *
239 * @param type
240 * 验证码类型,参见本类的静态属性
241 * @param length
242 * 验证码长度,大于0的整数
243 * @param exChars
244 * 需排除的特殊字符(仅对数字、字母混合型验证码有效,无需排除则为null)
245 * @return 验证码字符串
246 */
247 public static String generateTextCode(int type, int length, String exChars) {
248
249 if (length <= 0) {
250 return "";
251 }
252 StringBuffer code = new StringBuffer();
253 int i = 0;
254 Random r = new Random();
255
256 switch (type) {
257
258 // 仅数字
259 case TYPE_NUM_ONLY:
260 while (i < length) {
261 int t = r.nextInt(10);
262 // 排除特殊字符
263 if (exChars == null || exChars.indexOf(t + "") < 0) {
264 code.append(t);
265 i++;
266 }
267 }
268 break;
269
270 // 仅字母(即大写字母、小写字母混合)
271 case TYPE_LETTER_ONLY:
272 while (i < length) {
273 int t = r.nextInt(123);
274 if ( ( t >= 97 || ( t >= 65 && t <= 90 ) ) && ( exChars == null || exChars.indexOf((char) t) < 0 )) {
275 code.append((char) t);
276 i++;
277 }
278 }
279 break;
280
281 // 数字、大写字母、小写字母混合
282 case TYPE_ALL_MIXED:
283 while (i < length) {
284 int t = r.nextInt(123);
285 if ( ( t >= 97 || ( t >= 65 && t <= 90 ) || ( t >= 48 && t <= 57 ) ) && ( exChars == null || exChars.indexOf((char) t) < 0 )) {
286 code.append((char) t);
287 i++;
288 }
289 }
290 break;
291
292 // 数字、大写字母混合
293 case TYPE_NUM_UPPER:
294 while (i < length) {
295 int t = r.nextInt(91);
296 if ( ( t >= 65 || ( t >= 48 && t <= 57 ) ) && ( exChars == null || exChars.indexOf((char) t) < 0 )) {
297 code.append((char) t);
298 i++;
299 }
300 }
301 break;
302
303 // 数字、小写字母混合
304 case TYPE_NUM_LOWER:
305 while (i < length) {
306 int t = r.nextInt(123);
307 if ( ( t >= 97 || ( t >= 48 && t <= 57 ) ) && ( exChars == null || exChars.indexOf((char) t) < 0 )) {
308 code.append((char) t);
309 i++;
310 }
311 }
312 break;
313
314 // 仅大写字母
315 case TYPE_UPPER_ONLY:
316 while (i < length) {
317 int t = r.nextInt(91);
318 if ( ( t >= 65 ) && ( exChars == null || exChars.indexOf((char) t) < 0 )) {
319 code.append((char) t);
320 i++;
321 }
322 }
323 break;
324
325 // 仅小写字母
326 case TYPE_LOWER_ONLY:
327 while (i < length) {
328 int t = r.nextInt(123);
329 if ( ( t >= 97 ) && ( exChars == null || exChars.indexOf((char) t) < 0 )) {
330 code.append((char) t);
331 i++;
332 }
333 }
334 break;
335 default:
336 break;
337 }
338
339 return code.toString();
340 }
341
342 /**
343 * 已有验证码,生成验证码图片
344 *
345 * @param textCode
346 * 文本验证码
347 * @param width
348 * 图片宽度
349 * @param height
350 * 图片高度
351 * @param interLine
352 * 图片中干扰线的条数
353 * @param randomLocation
354 * 每个字符的高低位置是否随机
355 * @param backColor
356 * 图片颜色,若为null,则采用随机颜色
357 * @param foreColor
358 * 字体颜色,若为null,则采用随机颜色
359 * @param lineColor
360 * 干扰线颜色,若为null,则采用随机颜色
361 * @return 图片缓存对象
362 */
363 public static BufferedImage generateImageCode(String textCode, int width, int height, int interLine, boolean randomLocation, Color backColor,
364 Color foreColor, Color lineColor) {
365
366 BufferedImage bim = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
367 Graphics g = bim.getGraphics();
368
369 // 画背景图
370 g.setColor(backColor == null ? getRandomColor() : backColor);
371 g.fillRect(0, 0, width, height);
372
373 // 画干扰线
374 Random r = new Random();
375 if (interLine > 0) {
376
377 int x = 0, y = 0, x1 = width, y1 = 0;
378 for (int i = 0; i < interLine; i++) {
379 g.setColor(lineColor == null ? getRandomColor() : lineColor);
380 y = r.nextInt(height);
381 y1 = r.nextInt(height);
382
383 g.drawLine(x, y, x1, y1);
384 }
385 }
386
387 // 写验证码
388
389 // g.setColor(getRandomColor());
390 // g.setColor(isSimpleColor?Color.BLACK:Color.WHITE);
391
392 // 字体大小为图片高度的80%
393 int fsize = (int) ( height * 0.8 );
394 int fx = height - fsize;
395 int fy = fsize;
396
397 g.setFont(new Font("Default", Font.PLAIN, fsize));
398
399 // 写验证码字符
400 for (int i = 0; i < textCode.length(); i++) {
401 // 每个字符高低是否随机
402 fy = randomLocation ? (int) ( ( Math.random() * 0.3 + 0.6 ) * height ) : fy;
403 g.setColor(foreColor == null ? getRandomColor() : foreColor);
404 g.drawString(textCode.charAt(i) + "", fx, fy);
405 fx += fsize * 0.9;
406 }
407
408 g.dispose();
409
410 return bim;
411 }
412
413 /**
414 * 生成图片验证码
415 *
416 * @param type
417 * 验证码类型,参见本类的静态属性
418 * @param length
419 * 验证码字符长度,大于0的整数
420 * @param exChars
421 * 需排除的特殊字符
422 * @param width
423 * 图片宽度
424 * @param height
425 * 图片高度
426 * @param interLine
427 * 图片中干扰线的条数
428 * @param randomLocation
429 * 每个字符的高低位置是否随机
430 * @param backColor
431 * 图片颜色,若为null,则采用随机颜色
432 * @param foreColor
433 * 字体颜色,若为null,则采用随机颜色
434 * @param lineColor
435 * 干扰线颜色,若为null,则采用随机颜色
436 * @return 图片缓存对象
437 */
438 public static BufferedImage generateImageCode(int type, int length, String exChars, int width, int height, int interLine, boolean randomLocation,
439 Color backColor, Color foreColor, Color lineColor) {
440 String textCode = generateTextCode(type, length, exChars);
441 BufferedImage bim = generateImageCode(textCode, width, height, interLine, randomLocation, backColor, foreColor, lineColor);
442
443 return bim;
444 }
445
446 /**
447 * 产生随机颜色
448 *
449 * @return
450 */
451 private static Color getRandomColor() {
452 Random r = new Random();
453 Color c = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
454 return c;
455 }
456
457 }