Java實現驗證碼的制作


驗證碼概述

為什么使用驗證碼?

  驗證碼(CAPTCHA)是一種全自動程序。主要是為了區分“進行操作的是不是人”。如果沒有驗證碼機制,將會導致以下的問題:

  • 對特定網站不斷進行登錄,破解密碼;
  • 對某個網站創建賬戶;
  • 對某個網站提交垃圾數據(灌水貼);
  • 對某個網站進行刷票。

 

使用Servlet實現驗證碼

  一個驗證碼包含兩個部分:圖片和輸入框。

 1 <script type="text/javascript">
 2     function reloadCode(){
 3         var time = new Date();
 4         // 給URL傳遞參數可以清空瀏覽器的緩存,讓瀏覽器認為這是一個新的請求
 5         document.getElementById('safecode').src = '<%=request.getContextPath()%>/servlet/ImageServlet?d=' + time;
 6     }
 7 </script>    
 8 
 9 <form action="<%=request.getContextPath()%>/servlet/ValidateImageServlet"method="post">
10         驗證碼:<img src="<%=request.getContextPath()%>/servlet/ImageServlet" alt="驗證碼" id="safecode">
11     <input type="text" id="verifyCode" name="verifyCode" size="6" />
12     <a href="javascript:reloadCode();">看不清楚</a><br>
13     <input type="submit" value="登錄" />
14 </form>

  我們用ImageServlet實時生成圖片。生成圖片所需要的步驟如下:

1 定義BufferedImage對象
2 獲得Graphics對象
3 聽過Random類產生隨機驗證碼信息
4 使用Graphics繪制圖片
5 記錄驗證碼信息到session中
6 使用ImageIO輸出圖片

  檢驗驗證碼是否正確:ValidateImageServlet

1 獲取頁面的驗證碼
2 獲取session中保存的驗證碼
3 比較驗證碼
4 返回校驗結果

  驗證的流程如下:

  生成驗證碼的ImageServlet:

 1 private static Random r = new Random();
 2     private static char[] chs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
 3     private static final int NUMBER_OF_CHS = 4;
 4     private static final int IMG_WIDTH = 65;
 5     private static final int IMG_HEIGHT = 25;
 6     
 7     
 8     public void doGet(HttpServletRequest request, HttpServletResponse response)
 9             throws ServletException, IOException {
10 
11             BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);    // 實例化BufferedImage
12             Graphics g = image.getGraphics();
13             Color c = new Color(200, 200, 255);                                             // 驗證碼圖片的背景顏色                                        
14             g.setColor(c);
15             g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);                                        // 圖片的邊框
16             
17             StringBuffer sb = new StringBuffer();                                           // 用於保存驗證碼字符串
18             int index;                                                                      // 數組的下標
19             for (int i = 0; i < NUMBER_OF_CHS; i++) {
20                 index = r.nextInt(chs.length);                                              // 隨機一個下標
21                 g.setColor(new Color(r.nextInt(88), r.nextInt(210), r.nextInt(150)));       // 隨機一個顏色
22                 g.drawString(chs[index] + "", 15 * i + 3, 18);                              // 畫出字符
23                 sb.append(chs[index]);                                                      // 驗證碼字符串
24             }
25             
26             request.getSession().setAttribute("piccode", sb.toString());                    // 將驗證碼字符串保存到session中
27             ImageIO.write(image, "jpg", response.getOutputStream());                        // 向頁面輸出圖像
28     }
29 
30     public void doPost(HttpServletRequest request, HttpServletResponse response)
31             throws ServletException, IOException {
32         doGet(request, response);
33     }
34 
35 }

  進行驗證碼圖片驗證的Servlet:

 1 public class ValidateImageServlet extends HttpServlet {
 2 
 3     public void doGet(HttpServletRequest request, HttpServletResponse response)
 4             throws ServletException, IOException {
 5         
 6         doPost(request, response);
 7     }
 8 
 9     public void doPost(HttpServletRequest request, HttpServletResponse response)
10             throws ServletException, IOException {
11 
12         response.setContentType("text/html;charset=utf-8");
13         String picString = (String) request.getSession().getAttribute("piccode");
14         String checkCode = request.getParameter("verifyCode");
15         PrintWriter out = response.getWriter();
16         if (picString.toUpperCase().equals(checkCode.toUpperCase()))
17             out.println("驗證碼正確");
18         else
19             out.print("驗證碼錯誤!");
20         
21         out.flush();
22         out.close();
23     }
24 
25 }

 開源組件實現驗證碼

  Jcaptcha:

  一個用來生成圖形驗證碼的開源組件,可以產生多種形式的驗證碼。可以與Spring組合使用。需要導入的jar包如下:

  用於展示驗證碼的auth_code_captcha.jsp如下:

1 <form action="submit.action" method="post">
2      <img src="jcaptcha.jpg" /> <input type="text" name="japtcha" value="" />
3      <input type="submit"/>
4 </form>

  web.xml的配置如下:

 1 <servlet>
 2     <servlet-name>jcaptcha</servlet-name>
 3     <servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class>
 4 </servlet>
 5 <!-- 處理表單提交的Servlet -->
 6 <servlet>
 7     <servlet-name>submit</servlet-name>
 8     <servlet-class>org.gpf.servlet.SubmitActionServlet</servlet-class>
 9 </servlet>
10 <servlet-mapping>
11     <servlet-name>jcaptcha</servlet-name>
12     <url-pattern>/jcaptcha.jpg</url-pattern>
13 </servlet-mapping>
14 <servlet-mapping>
15     <servlet-name>submit</servlet-name>
16     <url-pattern>/submit.action</url-pattern>
17 </servlet-mapping>

  表單提交的Servlet:

 1 package org.gpf.servlet;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 import com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet;
11 /**
12  * 圖片驗證碼的captcha實現
13  * @author gaopengfei
14  * @date 2015-5-20 下午9:58:20
15  */
16 public class SubmitActionServlet extends HttpServlet {
17     
18     private static final long serialVersionUID = 1L;
19 
20     protected void doPost(HttpServletRequest request,
21             HttpServletResponse response) throws ServletException, IOException {
22         
23         String userCaptchaResponse = request.getParameter("japtcha");
24         boolean captchaPassed = SimpleImageCaptchaServlet.validateResponse(request, userCaptchaResponse);
25         
26         response.setContentType("text/html;charset=utf-8");
27         if (captchaPassed)
28             response.getWriter().write("驗證通過!");
29         else {
30             response.getWriter().write("驗證失敗!");
31         }
32         response.getWriter().write("<br/><a href='auth_code_captcha.jsp'>重新驗證</a>");
33     }
34 }

  

  kaptcha:

  它是可以配置的,也可以生成各種樣式的驗證碼。如下是其簡單應用:用於顯示驗證碼的index.jsp

1 <img alt="驗證碼圖片" src="random.jpg">
2 <form action="check.jsp" method="post">
3     <input type="text" name="imageText">
4     <input type="submit" value="驗證">
5 </form>

  用於驗證驗證碼的check.jsp

 1 <%@ page import="com.google.code.kaptcha.Constants" %>
 2 <%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
 3 <%
 4     String myImageText = request.getParameter("imageText");
 5     String key = (String)request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
 6     
 7     if(!myImageText.isEmpty() && myImageText.equals(key))
 8         out.print("驗證通過!<br />");
 9     else
10         out.print("驗證失敗!<br />");    
11     out.print("你輸入的字符:" + myImageText + ",驗證碼字符:" + key);    
12  %>

  配置圖片顯示的Servlet:

1 <servlet>
2     <servlet-name>Kcaptcha</servlet-name>
3     <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
4 </servlet>
5 <servlet-mapping>
6     <servlet-name>Kcaptcha</servlet-name>
7     <url-pattern>/random.jpg</url-pattern>
8 </servlet-mapping>

  

Kaptcha的詳細配置

  1 <servlet>
  2     <servlet-name>Kcaptcha</servlet-name>
  3     <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
  4     
  5     <!-- 邊框 -->
  6     <init-param>
  7         <description>圖片邊框,yes(默認值)或者no</description>
  8         <param-name>kaptcha.border</param-name>
  9         <param-value>yes</param-value>
 10     </init-param>
 11     <init-param>
 12         <description>邊框顏色,white、black(默認)、blue等</description>
 13         <param-name>kaptcha.border.color</param-name>
 14         <param-value>green</param-value>
 15     </init-param>
 16     <init-param>
 17         <description>邊框厚度大於0</description>
 18         <param-name>kaptcha.border.thickness</param-name>
 19         <param-value>10</param-value>
 20     </init-param>
 21     
 22     <!-- 圖片寬高 -->
 23     <init-param>
 24         <description>圖片寬度</description>
 25         <param-name>kaptcha.image.width</param-name>
 26         <param-value>200</param-value>
 27     </init-param>
 28     <init-param>
 29         <description>圖片高度</description>
 30         <param-name>kaptcha.image.height</param-name>
 31         <param-value>60</param-value>
 32     </init-param>
 33 
 34     <!-- 圖片樣式 -->
 35     <init-param>
 36         <description>圖片樣式:水紋(WaterRipple)、魚眼(FishEyeGimpy)、陰影(ShadowGimpy)</description>
 37         <param-name>kaptcha.obscurificator.impl</param-name>
 38         <param-value>com.google.code.kaptcha.impl.ShadowGimpy</param-value>
 39     </init-param>
 40     
 41     <!-- 背景 -->
 42     <init-param>
 43         <description>背景實現類</description>
 44         <param-name>kaptcha.background.impl</param-name>
 45         <param-value>com.google.code.kaptcha.impl.DefaultBackground</param-value>
 46     </init-param>
 47     <init-param>
 48         <description>背景顏色漸變,指定開始顏色</description>
 49         <param-name>kaptcha.background.clear.from</param-name>
 50         <param-value>yellow</param-value>
 51     </init-param>
 52     <init-param>
 53         <description>背景顏色漸變,指定結束顏色</description>
 54         <param-name>kaptcha.background.clear.to</param-name>
 55         <param-value>red</param-value>
 56     </init-param>
 57     
 58     <!-- 文本 -->
 59     <init-param>
 60         <description>文本集合,驗證碼文字從此集合中獲取</description>
 61         <param-name>kaptcha.textproducer.char.string</param-name>
 62         <param-value>0123456789</param-value>
 63     </init-param>
 64     <init-param>
 65         <description>驗證碼長度</description>
 66         <param-name>kaptcha.textproducer.char.length</param-name>
 67         <param-value>6</param-value>
 68     </init-param>
 69     <init-param>
 70         <description>文字間隔</description>
 71         <param-name>kaptcha.textproducer.char.space</param-name>
 72         <param-value>2</param-value>
 73     </init-param>
 74     <!-- 字體 -->
 75     <init-param>
 76         <description>字體Arial,Courier</description>
 77         <param-name>kaptcha.textproducer.font.names</param-name>
 78         <param-value>Arial,Courier</param-value>
 79     </init-param>
 80     <init-param>
 81         <description>字體大小</description>
 82         <param-name>kaptcha.textproducer.font.size</param-name>
 83         <param-value>40</param-value>
 84     </init-param>
 85     <init-param>
 86         <description>字體顏色,white、black(默認)、blue等</description>
 87         <param-name>kaptcha.textproducer.font.color</param-name>
 88         <param-value>pink</param-value>
 89     </init-param>
 90 
 91     <init-param>
 92         <description>文字渲染器</description>
 93         <param-name>kaptcha.word.impl</param-name>
 94         <param-value>com.google.code.kaptcha.text.impl.DefaultWordRenderer</param-value>
 95     </init-param>
 96 
 97     <!-- 圖片和文本的實現類 -->
 98     <init-param>
 99         <description>圖片實現類,可以重寫這個類實現我們自己的圖片</description>
100         <param-name>kaptcha.producer.impl</param-name>
101         <param-value>com.google.code.kaptcha.impl.DefaultKaptcha</param-value>
102     </init-param>
103     <init-param>
104         <description>文本實現類</description>
105         <param-name>kaptcha.textproducer.impl</param-name>
106         <param-value>com.google.code.kaptcha.text.impl.DefaultTextCreator</param-value>
107     </init-param>
108     
109     <!-- 干擾 -->
110     <init-param>
111         <description>干擾實現類</description>
112         <param-name>kaptcha.noise.impl</param-name>
113         <param-value>com.google.code.kaptcha.impl.DefaultNoise</param-value>
114     </init-param>
115     <init-param>
116         <description>干擾顏色,合法值r,g,b或者white、black、blue</description>
117         <param-name>kaptcha.noise.color</param-name>
118         <param-value>255,0,0</param-value>
119     </init-param>
120     
121     <init-param>
122         <description>session中存放驗證碼的key鍵</description>
123         <param-name>kaptcha.session.key</param-name>
124         <param-value>KAPTCHA_SESSION_KEY</param-value>
125     </init-param>
126 </servlet>
127 <servlet-mapping>
128     <servlet-name>Kcaptcha</servlet-name>
129     <url-pattern>/random.jpg</url-pattern>
130 </servlet-mapping>

  中文驗證碼

  查看前面的配置發現驗證碼字符的生成主要依靠的是kaptcha.textproducer.impl這個文本實現類,查看com.google.code.kaptcha.text.impl.DefaultTextCreator的源碼,發現了它繼承自 Configurable並實現了TextProducer接口。我們可以仿照它自定義我們自己的驗證碼中的文本生成器:

 1 /**
 2  * 中文驗證碼的實現類
 3  */
 4 public class ChineseTextCreator extends Configurable implements TextProducer {
 5 
 6     @Override
 7     public String getText() {
 8 
 9         int length = getConfig().getTextProducerCharLength();
10         String finalWord = "", firstWord = "";
11         int tempInt = 0;
12         String[] array = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
13                 "a", "b", "c", "d", "e", "f" };
14 
15         Random rand = new Random();
16 
17         for (int i = 0; i < length; i++) {
18             switch (rand.nextInt(array.length)) {
19             case 1:
20                 tempInt = rand.nextInt(26) + 65;
21                 firstWord = String.valueOf((char) tempInt);
22                 break;
23             case 2:
24                 int r1,
25                 r2,
26                 r3,
27                 r4;
28                 String strH,
29                 strL;// high&low
30                 r1 = rand.nextInt(3) + 11; // 前閉后開[11,14)
31                 if (r1 == 13) {
32                     r2 = rand.nextInt(7);
33                 } else {
34                     r2 = rand.nextInt(16);
35                 }
36 
37                 r3 = rand.nextInt(6) + 10;
38                 if (r3 == 10) {
39                     r4 = rand.nextInt(15) + 1;
40                 } else if (r3 == 15) {
41                     r4 = rand.nextInt(15);
42                 } else {
43                     r4 = rand.nextInt(16);
44                 }
45 
46                 strH = array[r1] + array[r2];
47                 strL = array[r3] + array[r4];
48 
49                 byte[] bytes = new byte[2];
50                 bytes[0] = (byte) (Integer.parseInt(strH, 16));
51                 bytes[1] = (byte) (Integer.parseInt(strL, 16));
52 
53                 firstWord = new String(bytes);
54                 break;
55             default:
56                 tempInt = rand.nextInt(10) + 48;
57                 firstWord = String.valueOf((char) tempInt);
58                 break;
59             }
60             finalWord += firstWord;
61         }
62         return finalWord;
63     }
64 }

  只需要在web.xml中將初始化參數由默認的文本實現類改成我們自己的實現類:

1 <init-param>
2     <description>文本實現類</description>
3     <param-name>kaptcha.textproducer.impl</param-name>
4     <param-value>ChineseTextCreator</param-value>
5 </init-param>

  算式驗證碼

實現步驟如下:

  • 獲取隨機的數值將結果相加
  • 將計算公式寫入到驗證碼圖片
  • 將相加的結果放入到session中

因此,我們需要重寫KaptchaServlet這個用於生成驗證碼的Servlet。

 1 public class MyKaptchaServlet extends HttpServlet implements Servlet{
 2     
 3     private Properties props;
 4     private Producer kaptchaProducer;
 5     private String sessionKeyValue;
 6     
 7     public MyKaptchaServlet() {
 8          props = new Properties();
 9          kaptchaProducer = null;
10          sessionKeyValue = null;
11     }
12     
13     public void init(ServletConfig conf) throws ServletException {
14         super.init(conf);
15 
16         ImageIO.setUseCache(false);
17 
18         Enumeration initParams = conf.getInitParameterNames();
19         while (initParams.hasMoreElements()) {
20             String key = (String) initParams.nextElement();
21             String value = conf.getInitParameter(key);
22             this.props.put(key, value);
23         }
24 
25         Config config = new Config(this.props);
26         this.kaptchaProducer = config.getProducerImpl();
27         this.sessionKeyValue = config.getSessionKey();
28     }
29 
30     public void doGet(HttpServletRequest req, HttpServletResponse resp)
31             throws ServletException, IOException {
32         resp.setDateHeader("Expires", 0L);
33 
34         resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
35 
36         resp.addHeader("Cache-Control", "post-check=0, pre-check=0");
37 
38         resp.setHeader("Pragma", "no-cache");
39 
40         resp.setContentType("image/jpeg");
41 
42         String capText = this.kaptchaProducer.createText();
43         String s1 = capText.substring(0, 1);    // 獲取隨機生成的第一個數字
44         String s2 = capText.substring(1, 2);    // 由於web.xml中配置的驗證碼字符都是數字,不會發生數字格式化異常
45         int r = Integer.parseInt(s1) + Integer.parseInt(s2);
46 
47         req.getSession().setAttribute(this.sessionKeyValue, String.valueOf(r));    // 將結果存入session
48 
49         BufferedImage bi = this.kaptchaProducer.createImage(s1 + " + " + s2 + " = ?"); // 產生圖片
50 
51         ServletOutputStream out = resp.getOutputStream();
52 
53         ImageIO.write(bi, "jpg", out);
54         try {
55             out.flush();
56         } finally {
57             out.close();
58         }
59     }
60 
61 }

  在web.xml中有2點需要注意:

  1. 驗證碼文本只能是0~9這10個數字;
  2. 驗證碼的長度是2(寫多了也只會取出前2個隨機數,寫少了會拋出數字格式化異常)。
 1 <servlet-name>Kcaptcha</servlet-name>
 2     <servlet-class>MyKaptchaServlet</servlet-class>
 3 <init-param>
 4         <description>文本集合</description>
 5         <param-name>kaptcha.textproducer.char.string</param-name>
 6         <param-value>0123456789</param-value>
 7     </init-param>
 8     <init-param>
 9     <description>驗證碼長度</description>
10     <param-name>kaptcha.textproducer.char.length</param-name>
11     <param-value>8</param-value>
12 </init-param>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM