服務(wù)器程序接收到表單數(shù)據(jù)后,首先判斷用戶(hù)是否填寫(xiě)了正確的驗(yàn)證碼,只有該驗(yàn)證碼與服務(wù)器端保存的驗(yàn)證碼匹配時(shí),服務(wù)器程序才開(kāi)始正常的表單處理流程。驗(yàn)證碼使用一次即失效,
用戶(hù)只能重新向服務(wù)器發(fā)出訪問(wèn)表單填寫(xiě)頁(yè)面的請(qǐng)求來(lái)獲得新的驗(yàn)證碼,并填寫(xiě)新的驗(yàn)證碼后才能再次提交有效的表單請(qǐng)求,
這樣將大大
增加了用戶(hù)重復(fù)操作的難度。密碼猜測(cè)工具要逐一嘗試每個(gè)密碼的前題條件是先輸入正確的驗(yàn)證碼,而驗(yàn)證碼是一次性有效的,這樣基本上就阻斷了密碼猜測(cè)工具的自動(dòng)地處理過(guò)程
。
下面編寫(xiě)一個(gè)利用
Session
實(shí)現(xiàn)一次性驗(yàn)證碼的例子程序,整個(gè)程序包含
三個(gè)組件:
check_code.html
、
CheckCodeServlet.java
和
LogonFormServlet.java
。
check_code.html
是引用驗(yàn)證碼圖片的
FORM
表單頁(yè)面,
CheckCodeServlet.java
是用于產(chǎn)生帶有隨機(jī)驗(yàn)證碼圖片的
Servlet
程序,
LogonFormServlet.java
則是負(fù)責(zé)處理
FORM
表單請(qǐng)求的
Servlet
程序。
:
動(dòng)手體驗(yàn):
利用
Session
實(shí)現(xiàn)一次性驗(yàn)證碼
(
1
)
按上面描述的功能編寫(xiě)如
例程7-11、例程7-12和例程7-13所示的
程序。
例程7-11
?
check_code.html
?
<h3>
帶有驗(yàn)證碼的登錄頁(yè)面</h3>
<form action="servlet/LogonFormServlet" method="post">
用戶(hù)名:<input type="text" name="name"><br>
密 碼:<input type="password" name="pass"><br>
驗(yàn)證碼:<input type="text" name="check_code">
<img src="servlet/CheckCodeServlet"><br>
<input type="submit" value="
登錄">
</form>
?
例程7-12
?
CheckCodeServlet
.java
?
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
?
public class CheckCodeServlet extends HttpServlet
{
?????? private static int WIDTH = 60;
?????? private static int HEIGHT = 20;
?????? public void doGet(HttpServletRequest request,HttpServletResponse response)
???????????????????? throws ServletException,IOException
?????? {???????????
????????????? HttpSession session = request.getSession();
????????????? response.setContentType("image/jpeg");
????????????? ServletOutputStream sos = response.getOutputStream();
?
????????????? //
設(shè)置瀏覽器不要緩存此圖片
????????????? response.setHeader("Pragma","No-cache");
????????????? response.setHeader("Cache-Control","no-cache");
????????????? response.setDateHeader("Expires", 0);
?????????????
????????????? //
創(chuàng)建內(nèi)存圖象并獲得其圖形上下文
????????????? BufferedImage image =
???????????????????? new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
????????????? Graphics g = image.getGraphics();
?????????????
????????????? //
產(chǎn)生隨機(jī)的認(rèn)證碼
????????????? char [] rands = generateCheckCode();
?????????????
????????????? //
產(chǎn)生圖像
????????????? drawBackground(g);
????????????? drawRands(g,rands);
?????????????
????????????? //
結(jié)束圖像
的繪制
過(guò)程,
完成圖像
????????????? g.dispose();
?????????????
????????????
?
????????????????? //
將圖像輸出到客戶(hù)端
????????????? ByteArrayOutputStream bos = new ByteArrayOutputStream();
????????????? ImageIO.write(image, "JPEG", bos);
????????????? byte [] buf = bos.toByteArray();
????????????? response.setContentLength(buf.length);
????????????? //
下面的語(yǔ)句也可寫(xiě)成:
bos.writeTo(sos);
????????????? sos.write(buf);
????????????? bos.close();
????????????? sos.close();
?
????????????? //
將當(dāng)前驗(yàn)證碼存入到
Session
中
????????????? session.setAttribute("check_code",new String(rands));
????????????? //
直接使用下面的代碼將有問(wèn)題,
Session
對(duì)象必須在提交響應(yīng)前獲得
????????????? //request.getSession().setAttribute("check_code",new String(rands));
?????? }
??????
?????? private char [] generateCheckCode()
?????? {
????????????? //
定義驗(yàn)證碼的字符表
????????????? String chars = "0123456789abcdefghijklmnopqrstuvwxyz";
????????????? char [] rands = new char[4];
????????????? for(int i=0; i<4; i++)
????????????? {
???????????????????? int rand = (int)(Math.random() * 36);
???????????????????? rands[i] = chars.charAt(rand);
????????????? }
????????????? return rands;
?????? }
??????
?????? private void drawRands(Graphics g , char [] rands)
?????? {
????????????? g.setColor(Color.BLACK);
????????????? g.setFont(new Font(null,Font.ITALIC|Font.BOLD,18));
????????????? //
在不同的高度上輸出驗(yàn)證碼的每個(gè)字符
????????
????????????? g.drawString("" + rands[0],1,17);
????????????? g.drawString("" + rands[1],16,15);
????????????? g.drawString("" + rands[2],31,18);
????????????? g.drawString("" + rands[3],46,16);
????????????? System.out.println(rands);
?????? }
??????
?????? private void drawBackground(Graphics g)
?????? {
???????????? //
畫(huà)背景
????????????? g.setColor(new Color(0xDCDCDC));
????????????? g.fillRect(0, 0, WIDTH, HEIGHT);
????????????? //
隨機(jī)產(chǎn)生
120
個(gè)干擾點(diǎn)
????????????? for(int i=0; i<120; i++)
????????????? {
???????????????????? int x = (int)(Math.random() * WIDTH);
???????????????????? int y = (int)(Math.random() * HEIGHT);
???????????????????? int red = (int)(Math.random() * 255);
???????????????????? int green = (int)(Math.random() * 255);
???????????????????? int blue = (int)(Math.random() * 255);
???????????????????? g.setColor(new Color(red,green,blue));???????
???????????????????? g.drawOval(x,y,1,0);
????????????? }
?????? }
}
?
例程
7-13
?
LogonFormServlet
.java
?
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
?
public class LogonFormServlet extends HttpServlet
{
?????? public void service(HttpServletRequest request,
????????????? HttpServletResponse response) throws ServletException, IOException
?????? {
????????????? response.setContentType("text/html;charset=GB2312");?????????????????????????
????????????? PrintWriter out = response.getWriter();
?????????????
????????????? HttpSession session = request.getSession(false);
????????????? if(session == null)
????????????? {
???????????????????? out.println("
驗(yàn)證碼處理問(wèn)題
!");
???????????????????? return;
????????????? }
?????????????
????????????? String savedCode = (String)session.getAttribute("check_code");
????????????? if(savedCode == null)
????????????? {
???????????????????? out.println("
驗(yàn)證碼處理問(wèn)題
!");
???????????????????? return;
????????????? }
???????????????????????????
????????????? String checkCode = request.getParameter("check_code");
????????????? if(!savedCode.equals(checkCode))
????????????? {
???????????????????? /*
驗(yàn)證碼未通過(guò),不從
Session
中清除原來(lái)的驗(yàn)證碼,
????????????????????
以便用戶(hù)可以后退回登錄頁(yè)面繼續(xù)使用原來(lái)的驗(yàn)證碼進(jìn)行登錄
*/
???????????????????? out.println("
驗(yàn)證碼無(wú)效
!");
???????????????????? return;
????????????? }
????????????? /*
驗(yàn)證碼檢查通過(guò)后,從
Session
中清除原來(lái)的驗(yàn)證碼,
?????????????
以防用戶(hù)后退回登錄頁(yè)面繼續(xù)使用原來(lái)的驗(yàn)證碼進(jìn)行登錄
*/
????????????? session.removeAttribute("check_code");
????????????? out.println("
驗(yàn)證碼通過(guò),服務(wù)器正在校驗(yàn)用戶(hù)名和密碼
!");
?????? }
}
?
編譯上面的兩個(gè)
Java
源
文件,確保編譯后生成的class文件存放在了
<
tomcat
安裝目錄
>\webapps\it315\WEB-INF\classes
目錄中。將
check_code.html
文件保存在
<
tomcat
安裝目錄
>
\webapps\it315
目錄中。
(
2
)在
<
tomcat
安裝目錄
>
\webapps\it315\WEB-INF\web.xml
文件中注冊(cè)有關(guān)的Servlet,并設(shè)置其映射URL。在web.xml文件中的相應(yīng)位置處增加如下兩段內(nèi)容:
?????? <servlet>
????????????? <servlet-name>CheckCodeServlet</servlet-name>
????????????? <servlet-class>CheckCodeServlet</servlet-class>
?????? </servlet>???
?????? <servlet>
????????????? <servlet-name>LogonFormServlet</servlet-name>
????????????? <servlet-class>LogonFormServlet</servlet-class>
?????? </servlet>?
?????? ……
?????? <servlet-mapping>
????????????? <servlet-name>CheckCodeServlet</servlet-name>
????????????? <url-pattern>/servlet/CheckCodeServlet</url-pattern>
?????? </servlet-mapping>
?????? <servlet-mapping>
????????????? <servlet-name>LogonFormServlet</servlet-name>
????????????? <url-pattern>/servlet/LogonFormServlet</url-pattern>
?????? </servlet-mapping>?????????????????
保存
web.xml
文件后,重新啟動(dòng)
Tomcat
。
(
3
)在瀏覽器地址欄中輸入如下地址:
?????? http://localhost:8080/it315/check_code.html
瀏覽器中顯示出如圖
7.25
所示的效果
,然后就可以對(duì)驗(yàn)證碼的功能進(jìn)行測(cè)試了。