一、前言
在表單頁面中使用驗證碼的好處在于有效防止用戶惡意提交表單,或者使用外掛非法攻擊系統。
二、準備條件
1、一個普通的web項目 webProject;
2、一個web服務器 Tomcat。
三、實現思路:
1、自定義一個servlet VerifyCodeServlet 畫一個包含驗證字符的驗證碼圖片,這里的圖片需要使用Graphics2D手動去畫;
2、在具體頁面使用img標簽的src引用這個servlet即可顯示servlet;
3、因為畫圖的時候把驗證碼信息放入了session,所以提交表單后可以根據session中保存的值和用戶輸入的code做比較,驗證輸入是否正確。
網上大都是通過servlet實現的驗證碼,入下圖邏輯:
步驟:
1、請求登錄頁面時隨機生成驗證碼字符串;
2、將生成對驗證碼字符串存到session中;
3、根據驗證碼字符串生成驗證碼圖片,然后將驗證碼圖片輸出到客戶展示;
4、提交登錄請求時用戶輸入的驗證碼字符串與session中的字符串做比對。
四、具體代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
package com.servlet; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * 產生驗證碼圖片的servlet * @author Administrator * */ public class VerifyCodeServlet extends HttpServlet { private static final long serialVersionUID = -5051097528828603895L; /** * 驗證碼圖片的寬度。 */ private int width = 100 ; /** * 驗證碼圖片的高度。 */ private int height = 30 ; /** * 驗證碼字符個數 */ private int codeCount = 4 ; /** * 字體高度 */ private int fontHeight; /** * 第一個字符的x軸值,因為后面的字符坐標依次遞增,所以它們的x軸值是codeX的倍數 */ private int codeX; /** * codeY ,驗證字符的y軸值,因為并行所以值一樣 */ private int codeY; /** * codeSequence 表示字符允許出現的序列值 */ char [] codeSequence = { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' , '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' }; /** * 初始化驗證圖片屬性 */ public void init() throws ServletException { // 從web.xml中獲取初始信息 // 寬度 String strWidth = this .getInitParameter( "width" ); // 高度 String strHeight = this .getInitParameter( "height" ); // 字符個數 String strCodeCount = this .getInitParameter( "codeCount" ); // 將配置的信息轉換成數值 try { if (strWidth != null && strWidth.length() != 0 ) { width = Integer.parseInt(strWidth); } if (strHeight != null && strHeight.length() != 0 ) { height = Integer.parseInt(strHeight); } if (strCodeCount != null && strCodeCount.length() != 0 ) { codeCount = Integer.parseInt(strCodeCount); } } catch (NumberFormatException e) { e.printStackTrace(); } //width-4 除去左右多余的位置,使驗證碼更加集中顯示,減得越多越集中。 //codeCount+1 //等比分配顯示的寬度,包括左右兩邊的空格 codeX = (width- 4 ) / (codeCount+ 1 ); //height - 10 集中顯示驗證碼 fontHeight = height - 10 ; codeY = height - 7 ; } /** * @param request * @param response * @throws ServletException * @throws java.io.IOException */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { // 定義圖像buffer BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D gd = buffImg.createGraphics(); // 創建一個隨機數生成器類 Random random = new Random(); // 將圖像填充為白色 gd.setColor(Color.LIGHT_GRAY); gd.fillRect( 0 , 0 , width, height); // 創建字體,字體的大小應該根據圖片的高度來定。 Font font = new Font( "Fixedsys" , Font.PLAIN, fontHeight); // 設置字體。 gd.setFont(font); // 畫邊框。 gd.setColor(Color.BLACK); gd.drawRect( 0 , 0 , width - 1 , height - 1 ); // 隨機產生160條干擾線,使圖象中的認證碼不易被其它程序探測到。 gd.setColor(Color.gray); for ( int i = 0 ; i < 16 ; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt( 12 ); int yl = random.nextInt( 12 ); gd.drawLine(x, y, x + xl, y + yl); } // randomCode用于保存隨機產生的驗證碼,以便用戶登錄后進行驗證。 StringBuffer randomCode = new StringBuffer(); int red = 0 , green = 0 , blue = 0 ; // 隨機產生codeCount數字的驗證碼。 for ( int i = 0 ; i < codeCount; i++) { // 得到隨機產生的驗證碼數字。 String strRand = String.valueOf(codeSequence[random.nextInt( 36 )]); // 產生隨機的顏色分量來構造顏色值,這樣輸出的每位數字的顏色值都將不同。 red = random.nextInt( 255 ); green = random.nextInt( 255 ); blue = random.nextInt( 255 ); // 用隨機產生的顏色將驗證碼繪制到圖像中。 gd.setColor( new Color(red,green,blue)); gd.drawString(strRand, (i + 1 ) * codeX, codeY); // 將產生的四個隨機數組合在一起。 randomCode.append(strRand); } // 將四位數字的驗證碼保存到Session中。 HttpSession session = request.getSession(); session.setAttribute( "validateCode" , randomCode.toString()); // 禁止圖像緩存。 response.setHeader( "Pragma" , "no-cache" ); response.setHeader( "Cache-Control" , "no-cache" ); response.setDateHeader( "Expires" , 0 ); response.setContentType( "image/jpeg" ); // 將圖像輸出到Servlet輸出流中。 ServletOutputStream sos = response.getOutputStream(); ImageIO.write(buffImg, "jpeg" , sos); sos.close(); } } |
然后在web.xml中配置下這個生成驗證碼的servlet,具體如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< servlet > < servlet-name >VerifyCodeServlet</ servlet-name > < servlet-class >com.servlet.VerifyCodeServlet</ servlet-class > < init-param > < param-name >width</ param-name > < param-value >120</ param-value > </ init-param > < init-param > < param-name >height</ param-name > < param-value >32</ param-value > </ init-param > < init-param > < param-name >codeCount</ param-name > < param-value >4</ param-value > </ init-param > </ servlet > < servlet-mapping > < servlet-name >VerifyCodeServlet</ servlet-name > < url-pattern >/VerifyCodeServlet</ url-pattern > </ servlet-mapping > |
啟動服務器,在瀏覽器地址欄輸入:http://localhost:8080/webProject/VerifyCodeServlet
查看顯示效果,具體如下:
1、 每次刷新驗證碼都有變化,這是因為servlet設置了禁用瀏覽器緩存。
2、這里發現一個問題:如果使用火狐瀏覽器,VerifyCodeServlet中重寫的serviice方法被執行了兩次,換成重寫doGet方法或者doPost方法也是一樣, 并且其他瀏覽器都不見該情況,后來發現如果是通過頁面引用該servlet就調用正常了。
然后就可以在頁面中引用該驗證碼了,具體代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<%@ page language= "java" contentType= "text/html; charset=UTF-8" pageEncoding= "UTF-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta http-equiv= "Content-Type" content= "text/html; charset=ISO-8859-1" > <title>Insert title here</title> </head> <body> <div> <% String inputCode = request.getParameter( "inputCode" ); String verifyCode = (String)session.getAttribute( "validateCode" ); if (inputCode!= null && verifyCode!= null ){ out.print( "真正的驗證碼:" + verifyCode + "<br/>" + "用戶輸入的驗證碼:" + inputCode + "<br/>" ); inputCode = inputCode.toUpperCase(); //不區分大小寫 out.print( "比較驗證碼證明用戶輸入 " + (inputCode.equals(verifyCode)? "正確" : "錯誤" ) + "!" ); } %> <form action= "index.jsp" > 驗證碼:<input name= "inputCode" value= "" /> <img src= "VerifyCodeServlet" align= "middle" title= "看不清,請點我" onclick= "javascript:refresh(this);" onmouseover= "mouseover(this)" /><br/> <input name= "submit" type= "submit" value= "提交" /> </form> </div> <script> function refresh(obj){ obj.src = "VerifyCodeServlet?" + Math.random(); } function mouseover(obj){ obj.style.cursor = "pointer" ; } </script> </body> </html> |
上面代碼通過表單提交驗證碼到當前jsp,驗證用戶輸入的驗證碼是否正確,運行的具體效果如下:
1、輸入正確的驗證碼
2、輸入錯誤的驗證碼