自己写一个图片验证码程序

时间:2023-02-04 22:51:27

本程序基于struts2,用action响应请求。

一、首先,创建一个用于产生随即验证码图片的类ImageCode.java.

  1 package com.exp.image;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Font;
6 import java.awt.Graphics;
7 import java.awt.Graphics2D;
8 import java.awt.RenderingHints;
9 import java.awt.geom.AffineTransform;
10 import java.awt.image.BufferedImage;
11 import java.io.ByteArrayInputStream;
12 import java.io.ByteArrayOutputStream;
13 import java.io.File;
14 import java.io.FileNotFoundException;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.OutputStream;
18 import java.util.Random;
19
20 import javax.ejb.Init;
21 import javax.imageio.ImageIO;
22 import javax.imageio.stream.ImageOutputStream;
23
24 public class ImageCode {
25 private static Random random = new Random();// 随机数对象
26 private static ByteArrayInputStream inputStream;// 字节数组输入流对象
27 private static String randString;// 随机生成的字符串
28 private static ImageCode imageCode;// 本类对象
29 // 以下是系统默认参数
30 private static int nums = 6;// 验证码位数
31 private static int width = 80;// 图片宽
32 private static int height = 22;// 图片高
33 private static int left = 4;// 字符输出左边距
34 private static int top = 18;// 字符输出上边距
35 private static int fontSize = 22;// 字符输出上边距
36 private static String charSet = "0123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ";// 验证码字符集合
37 private static int stringMinColor = 100;// 字符最低颜色值
38 private static int stringMaxColor = 177;// 字符最高颜色值
39 private static int otherMinColor = 178;// 干扰线和噪点最低颜色值
40 private static int otherMaxColor = 255;// 干扰线和噪点最高颜色值
41 private static Color backgroundColor = Color.cyan;// 图片背景色
42 private static boolean isShear = false;// 是否进行字符扭曲
43
44 /**
45 * 单例模式
46 */
47 static {
48 if (imageCode == null) {
49 imageCode = new ImageCode();
50 }
51 }
52
53 /**
54 * 设置综合信息
55 *
56 * @param nums
57 * 验证码位数
58 * @param width
59 * 图片宽度,默认值80
60 * @param height
61 * 图片高度,默认值22
62 * @param left
63 * 字符输出左边距,默认4
64 * @param top
65 * 字符输出上边距,默认20
66 * @param fontSize
67 * 字符大小,默认22
68 * @param charSet
69 * 验证码字符集合字符串,默认为
70 * "0123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
71 * @param stringMinColor
72 * 字符输出最小颜色值,默认100
73 * @param stringMaxColor
74 * 字符输出最大颜色值,默认177
75 * @param otherMinColor
76 * 干扰线和噪点最小颜色值,默认178
77 * @param otherMaxColor
78 * 干扰线和噪点最大颜色值,默认255
79 * @param backgroundColor
80 * 图片背景色,默认Color.cyan
81 * @param isShear
82 * 是否进行单个字符扭曲,默认false
83 */
84 public static void set(int nums, int width, int height, int left, int top,
85 int fontSize, String charSet, int stringMinColor,
86 int stringMaxColor, int otherMinColor, int otherMaxColor,
87 Color backgroundColor,boolean isShear) {
88 // 设置验证码位数
89 if (nums != 0) {
90 ImageCode.nums = nums;
91 }
92 // 设置验证码图片宽度
93 if (width != 0) {
94 ImageCode.width = width;
95 }
96 // 设置验证码图片高度
97 if (height != 0) {
98 ImageCode.height = height;
99 }
100 // 设置验证码字符输出左边距
101 if (left != 0) {
102 ImageCode.left = left;
103 }
104 // 设置验证码字符输出上边距
105 if (top != 0) {
106 ImageCode.top = top;
107 }
108 // 设置验证码字符大小
109 if (fontSize != 0) {
110 ImageCode.fontSize = fontSize;
111 }
112 // 设置验证码字符集合字符串
113 if (charSet != null) {
114 ImageCode.charSet = charSet;
115 }
116 // 设置验证码字符输出最小颜色值
117 if (stringMinColor != 0) {
118 ImageCode.stringMinColor = stringMinColor;
119 }
120 // 设置验证码字符输出最大颜色值
121 if (stringMaxColor != 0) {
122 ImageCode.stringMaxColor = stringMaxColor;
123 }
124 // 设置验证码干扰线和噪点最小颜色值
125 if (otherMinColor != 0) {
126 ImageCode.otherMinColor = otherMinColor;
127 }
128 // 设置验证码干扰线和噪点最大颜色值
129 if (otherMaxColor != 0) {
130 ImageCode.otherMaxColor = otherMaxColor;
131 }
132 // 设置验证码图片背景色
133 if (backgroundColor != null) {
134 ImageCode.backgroundColor = backgroundColor;
135 }
136 // 设置验证码图片背景色
137 ImageCode.isShear = isShear;
138 }
139
140 /**
141 * 创建随机图片,生成随机字符串
142 */
143 public static void createRandomImage() {
144 // 建立内存图像
145 BufferedImage image = new BufferedImage(width, height,
146 BufferedImage.TYPE_INT_RGB);
147 // 创建图形上下文
148 Graphics2D g = image.createGraphics();
149 // 消除线段的锯齿边缘
150 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
151 RenderingHints.VALUE_ANTIALIAS_ON);
152 // 设置底色
153 g.setColor(Color.PINK);
154 // 绘制填充矩形
155 g.fillRect(0, 0, width, height);
156 // 绘制干扰线
157 for (int i = 0; i < 200; i++) {
158 g.setColor(getRandColor(otherMinColor, otherMaxColor));
159 g.setStroke(new BasicStroke(1f));
160 int x1 = random.nextInt(width);
161 int y1 = random.nextInt(height);
162 int x2 = random.nextInt(width * 2);
163 int y2 = random.nextInt(height * 2);
164 g.drawLine(x1, y1, x2, y2);
165 }
166
167 // 获得字符串
168 StringBuffer sBuffer = new StringBuffer();
169
170 // 按位数拼接随机字符串
171 for (int i = 0; i < nums; i++) {
172 // 根据随机数在charSet中寻找字符
173 sBuffer.append(charSet.charAt(random.nextInt(charSet.length())));
174 }
175 String code = sBuffer.toString();// 这个字符串是包含大小写字母的,不包括Oo
176 setRandString(code.toLowerCase());// 所有字母转小写
177
178 // 绘制字符串
179 if (isShear) {
180 // 字符逐个扭曲输出
181 shearEveryChar(g, code, 0, 127);
182 } else {
183 // 输出字符串,这是不进行单个字符扭曲的选择
184 g.setColor(getRandColor(stringMinColor, stringMaxColor));
185 g.setFont(new Font("黑体", Font.BOLD + Font.ITALIC, 22));
186 g.drawString(code, left, top);
187 }
188
189 // 绘制噪点
190 drawNoise(g, image, 0.05f);
191
192 // 扭曲整个图片
193 // shear(g, width, height, getRandColor(128, 255));// 使图片扭曲
194
195 // 设置输出流
196 ByteArrayOutputStream bos = new ByteArrayOutputStream();
197 try {
198 ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
199 ImageIO.write(image, "jpg", ios);
200 ByteArrayInputStream bis = new ByteArrayInputStream(
201 bos.toByteArray());
202 setInputStream(bis);
203 bos.flush();
204 bis.close();
205 ios.close();
206 } catch (IOException e) {
207 e.printStackTrace();
208 } finally {
209 try {
210 bos.flush();
211 bos.close();
212 } catch (IOException e) {
213 e.printStackTrace();
214 }
215 }
216
217 }
218
219 /**
220 * 绘制噪点
221 *
222 * @param g
223 * @param image
224 * @param yRote
225 */
226 public static void drawNoise(Graphics2D g, BufferedImage image, float yRote) {
227 int area = (int) (yRote * width * height);
228 for (int i = 0; i < area; i++) {
229 g.setStroke(new BasicStroke(2f));
230 int x = random.nextInt(width);
231 int y = random.nextInt(height);
232 int rgb = getRandomIntColor();
233 image.setRGB(x, y, rgb);
234 }
235 }
236
237 /**
238 * 字符逐个扭曲输出
239 *
240 * @param g
241 * @param code
242 */
243 public static void shearEveryChar(Graphics2D g, String code,
244 int stringMinColor, int stringMaxColor) {
245 int fontSize = height - 4;
246 Font font = new Font("黑体", Font.BOLD + Font.ITALIC, fontSize);
247 g.setFont(font);
248 char[] chars = code.toCharArray();
249 for (int i = 0; i < code.length(); i++) {
250 g.setColor(getRandColor(stringMinColor, stringMaxColor));
251 AffineTransform affine = new AffineTransform();
252 affine.setToRotation(
253 Math.PI / 4 * random.nextDouble()
254 * (random.nextBoolean() ? 1 : -1),
255 (width / code.length()) * i + fontSize / 2, height / 2);
256 g.setTransform(affine);
257 g.drawChars(chars, i, 1, ((width - 10) / code.length()) * i + 5,
258 height / 2 + fontSize / 2 - 3);
259 }
260 }
261
262 /**
263 * 获得随机颜色
264 *
265 * @param fc
266 * 颜色起始值
267 * @param bc
268 * 颜色终止值
269 * @return 返回Color对象
270 */
271 private static Color getRandColor(int fc, int bc) {
272 if (fc > 255)
273 fc = 255;
274 if (bc > 255)
275 bc = 255;
276 int r = fc + random.nextInt(bc - fc);
277 int g = fc + random.nextInt(bc - fc);
278 int b = fc + random.nextInt(bc - fc);
279 return new Color(r, g, b);
280 }
281
282 /**
283 * 获得整型随机色
284 *
285 * @return
286 */
287 private static int getRandomIntColor() {
288 int[] rgb = getRandomRgb();
289 int color = 0;
290 for (int c : rgb) {
291 color = color << 8;
292 color = color | c;
293 }
294 return color;
295 }
296
297 /**
298 * 获得随机数组,适配整数类型的颜色值
299 *
300 * @return
301 */
302 private static int[] getRandomRgb() {
303 int[] rgb = new int[3];
304 for (int i = 0; i < 3; i++) {
305 rgb[i] = random.nextInt(255);
306 }
307 return rgb;
308 }
309
310 // 整个图片扭曲
311 private static void shear(Graphics g, int w1, int h1, Color color) {
312 shearX(g, w1, h1, color);
313 shearY(g, w1, h1, color);
314 }
315
316 // X轴扭曲
317 private static void shearX(Graphics g, int w1, int h1, Color color) {
318 int period = random.nextInt(2);
319 boolean borderGap = true;
320 int frames = 1;
321 int phase = random.nextInt(2);
322
323 for (int i = 0; i < h1; i++) {
324 double d = (double) (period >> 1)
325 * Math.sin((double) i / (double) period
326 + (6.2831853071795862D * (double) phase)
327 / (double) frames);
328 g.copyArea(0, i, w1, 1, (int) d, 0);
329 if (borderGap) {
330 g.setColor(color);
331 g.drawLine((int) d, i, 0, i);
332 g.drawLine((int) d + w1, i, w1, i);
333 }
334 }
335
336 }
337
338 // Y轴扭曲
339 private static void shearY(Graphics g, int w1, int h1, Color color) {
340 int period = random.nextInt(40) + 10; // 50;
341 boolean borderGap = true;
342 int frames = 20;
343 int phase = 7;
344 for (int i = 0; i < w1; i++) {
345 double d = (double) (period >> 1)
346 * Math.sin((double) i / (double) period
347 + (6.2831853071795862D * (double) phase)
348 / (double) frames);
349 g.copyArea(i, 0, 1, h1, 0, (int) d);
350 if (borderGap) {
351 g.setColor(color);
352 g.drawLine(i, (int) d, i, 0);
353 g.drawLine(i, (int) d + h1, i, h1);
354 }
355
356 }
357
358 }
359
360 // 封装
361 public static ByteArrayInputStream getInputStream() {
362 return inputStream;
363 }
364
365 public static void setInputStream(ByteArrayInputStream inputStream) {
366 ImageCode.inputStream = inputStream;
367 }
368
369 public static String getRandString() {
370 return randString;
371 }
372
373 public static int getNums() {
374 return nums;
375 }
376
377 public static void setNums(int nums) {
378 ImageCode.nums = nums;
379 }
380
381 public static int getWidth() {
382 return width;
383 }
384
385 public static void setWidth(int width) {
386 ImageCode.width = width;
387 }
388
389 public static int getHeight() {
390 return height;
391 }
392
393 public static void setHeight(int height) {
394 ImageCode.height = height;
395 }
396
397 public static int getLeft() {
398 return left;
399 }
400
401 public static void setLeft(int left) {
402 ImageCode.left = left;
403 }
404
405 public static int getTop() {
406 return top;
407 }
408
409 public static void setTop(int top) {
410 ImageCode.top = top;
411 }
412
413 public static String getCharSet() {
414 return charSet;
415 }
416
417 public static void setCharSet(String charSet) {
418 ImageCode.charSet = charSet;
419 }
420
421 public static int getStringMinColor() {
422 return stringMinColor;
423 }
424
425 public static void setStringMinColor(int stringMinColor) {
426 ImageCode.stringMinColor = stringMinColor;
427 }
428
429 public static int getStringMaxColor() {
430 return stringMaxColor;
431 }
432
433 public static void setStringMaxColor(int stringMaxColor) {
434 ImageCode.stringMaxColor = stringMaxColor;
435 }
436
437 public static int getOtherMinColor() {
438 return otherMinColor;
439 }
440
441 public static void setOtherMinColor(int otherMinColor) {
442 ImageCode.otherMinColor = otherMinColor;
443 }
444
445 public static int getOtherMaxColor() {
446 return otherMaxColor;
447 }
448
449 public static void setOtherMaxColor(int otherMaxColor) {
450 ImageCode.otherMaxColor = otherMaxColor;
451 }
452
453 public static Color getBackgroundColor() {
454 return backgroundColor;
455 }
456
457 public static void setBackgroundColor(Color backgroundColor) {
458 ImageCode.backgroundColor = backgroundColor;
459 }
460
461 public static ImageCode getImageCode() {
462 return imageCode;
463 }
464
465 public static void setImageCode(ImageCode imageCode) {
466 ImageCode.imageCode = imageCode;
467 }
468
469 public static void setRandString(String randString) {
470 ImageCode.randString = randString;
471 }
472
473 public static int getFontSize() {
474 return fontSize;
475 }
476
477 public static void setFontSize(int fontSize) {
478 ImageCode.fontSize = fontSize;
479 }
480
481 public static boolean isShear() {
482 return isShear;
483 }
484
485 public static void setShear(boolean isShear) {
486 ImageCode.isShear = isShear;
487 }
488
489 }

基本思路:

1.用字符集合确定验证码显示的字符;

2.用参数确定各种控制数据;

3.生成一个字符串,赋予一个成员变量,用这个字符串循环绘制在画布上,给图片添加干扰线、噪点和文字扭曲等效果;

4.图片最终赋给一个字节数组输入流对象。

二、创建TestAction.java

 1 package com.exp.action;
2
3 import java.io.ByteArrayInputStream;
4
5 import javax.servlet.http.HttpServletResponse;
6
7 import com.exp.image.ImageCode;
8 import com.opensymphony.xwork2.ActionContext;
9
10 public class TestAction {
11 private String usercode;// 用户输入的验证码
12 private ByteArrayInputStream inputStream;// 图片输入流
13 private String info;
14
15 /**
16 * 登录
17 *
18 * @return
19 */
20 public String login() {
21 // 输出测试
22 System.out.println(ImageCode.getRandString());
23 if (ImageCode.getRandString().equals(usercode.toLowerCase())) {
24 System.out.println("没问题");
25 info = "验证码输入正确";
26 } else {
27 System.out.println("不对");
28 info = "验证码输入不正确";
29 }
30 return "test_login_success";
31 }
32
33 /**
34 * 获取图片验证码
35 *
36 * @return
37 */
38 public String getimagecode() {
39 // 创建图片验证码
40 ImageCode.createRandomImage();
41 ImageCode.setHeight(21);// 设置高度
42 // 输出测试
43 System.out.println(ImageCode.getRandString());
44 // 将图片输入自字节流赋予inputStream
45 this.setInputStream(ImageCode.getInputStream());
46 return "test_getimagecode_success";
47 }
48
49 // 封装
50 public String getUsercode() {
51 return usercode;
52 }
53
54 public void setUsercode(String usercode) {
55 this.usercode = usercode;
56 }
57
58 public ByteArrayInputStream getInputStream() {
59 return inputStream;
60 }
61
62 public void setInputStream(ByteArrayInputStream inputStream) {
63 this.inputStream = inputStream;
64 }
65
66 public String getInfo() {
67 return info;
68 }
69
70 public void setInfo(String info) {
71 this.info = info;
72 }
73 }

思路:使用单例模式,获取验证码字节数组输入流对象,并且声称验证码字符串。

三、配置struts.xml

 1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
3 <struts>
4 <package name="test" namespace="/" extends="struts-default">
5 <action name="*_*" class="com.exp.action.{1}Action" method="{2}">
6 <result name="test_login_success">/index2.jsp</result>
7 <!-- 图片验证码 -->
8 <result name="test_getimagecode_success" type="stream">
9 <param name="contentType">image/jpeg</param>
10 <param name="inputName">inputStream</param>
11 </result>
12 </action>
13 </package>
14 </struts>

思路:规定结果类型为流,设定参数类型为图片,设定inputName的值为TestAction中的字节数组输入流对象。

四、设计测试页面index2.jsp

 1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
2 <%@ taglib uri="/struts-tags" prefix="s" %>
3 <%
4 String path = request.getContextPath();
5 String basePath = request.getScheme() + "://"
6 + request.getServerName() + ":" + request.getServerPort()
7 + path + "/";
8 %>
9
10 <!DOCTYPE html>
11 <html>
12 <head>
13 <base href="<%=basePath%>">
14 <title>My JSP 'index.jsp' starting page</title>
15 <script type="text/javascript" src="js/jquery-2.1.4.js"></script>
16 </head>
17
18 <body>
19 <form action="Test_login" method="post">
20 <input id="othertext" placeholder="验证异步刷新" style="float:left;">
21 <input name="usercode" maxlength="6" style="float:left;"> <span style="display:block;float:left;width:80px;height:22px;border:0px solid green;"><img
22 id="randimg" src="Test_getimagecode" onclick="changeRandomImage();" style="border:0px solid red;"></span><br>
23 <input type="submit" value="OK" style="width:100px;height:21px;float:left;margin:-19px 0 0 10px;">
24 </form>
25 <div style="width:200px;height:22px;float:left;margin:-18px 0 0 10px;color:red;"><s:property value="info"/></div>
26
27 <script type="text/javascript">
28 //异步刷新图片验证码
29 function changeRandomImage() {
30 var imgSrc = $("#randimg");
31 var src = imgSrc.attr("src");
32 imgSrc.attr("src", changeGetImageUrl(src));
33 }
34 //时间戳
35 //为了使每次生成图片不一致,即不让浏览器读缓存,所以需要加上时间戳,让每次都有新的请求
36 function changeGetImageUrl(url) {
37 var timestamp = (new Date()).valueOf();
38 if ((url.indexOf("&") >= 0)) {
39 url = url + "×tamp=" + timestamp;
40 } else {
41 url = url + "?timestamp=" + timestamp;
42 }
43 return url;
44 }
45 </script>
46 </body>
47 </html>

思路:点击图片进行异步刷新。为了防止每次刷新时页面读取缓存导致不变,特意设置一个没有实际作用的时间戳为参数形成不同的请求,获得新的结果

五、运行效果

自己写一个图片验证码程序

自己写一个图片验证码程序