本程序基於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>
思路:點擊圖片進行異步刷新。為了防止每次刷新時頁面讀取緩存導致不變,特意設置一個沒有實際作用的時間戳為參數形成不同的請求,獲得新的結果
五、運行效果