服務器程序接收到表單數據后,首先判斷用戶是否填寫了正確的驗證碼,只有該驗證碼與服務器端保存的驗證碼匹配時,服務器程序才開始正常的表單處理流程。驗證碼使用一次即失效,
用戶只能重新向服務器發出訪問表單填寫頁面的請求來獲得新的驗證碼,并填寫新的驗證碼后才能再次提交有效的表單請求,
這樣將大大
增加了用戶重復操作的難度。密碼猜測工具要逐一嘗試每個密碼的前題條件是先輸入正確的驗證碼,而驗證碼是一次性有效的,這樣基本上就阻斷了密碼猜測工具的自動地處理過程
。
下面編寫一個利用
Session
實現一次性驗證碼的例子程序,整個程序包含
三個組件:
check_code.html
、
CheckCodeServlet.java
和
LogonFormServlet.java
。
check_code.html
是引用驗證碼圖片的
FORM
表單頁面,
CheckCodeServlet.java
是用于產生帶有隨機驗證碼圖片的
Servlet
程序,
LogonFormServlet.java
則是負責處理
FORM
表單請求的
Servlet
程序。
:
動手體驗:
利用
Session
實現一次性驗證碼
(
1
)
按上面描述的功能編寫如
例程7-11、例程7-12和例程7-13所示的
程序。
例程7-11
?
check_code.html
?
<h3>
帶有驗證碼的登錄頁面</h3>
<form action="servlet/LogonFormServlet" method="post">
用戶名:<input type="text" name="name"><br>
密 碼:<input type="password" name="pass"><br>
驗證碼:<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();
?
????????????? //
設置瀏覽器不要緩存此圖片
????????????? response.setHeader("Pragma","No-cache");
????????????? response.setHeader("Cache-Control","no-cache");
????????????? response.setDateHeader("Expires", 0);
?????????????
????????????? //
創建內存圖象并獲得其圖形上下文
????????????? BufferedImage image =
???????????????????? new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
????????????? Graphics g = image.getGraphics();
?????????????
????????????? //
產生隨機的認證碼
????????????? char [] rands = generateCheckCode();
?????????????
????????????? //
產生圖像
????????????? drawBackground(g);
????????????? drawRands(g,rands);
?????????????
????????????? //
結束圖像
的繪制
過程,
完成圖像
????????????? g.dispose();
?????????????
????????????
?
????????????????? //
將圖像輸出到客戶端
????????????? ByteArrayOutputStream bos = new ByteArrayOutputStream();
????????????? ImageIO.write(image, "JPEG", bos);
????????????? byte [] buf = bos.toByteArray();
????????????? response.setContentLength(buf.length);
????????????? //
下面的語句也可寫成:
bos.writeTo(sos);
????????????? sos.write(buf);
????????????? bos.close();
????????????? sos.close();
?
????????????? //
將當前驗證碼存入到
Session
中
????????????? session.setAttribute("check_code",new String(rands));
????????????? //
直接使用下面的代碼將有問題,
Session
對象必須在提交響應前獲得
????????????? //request.getSession().setAttribute("check_code",new String(rands));
?????? }
??????
?????? private char [] generateCheckCode()
?????? {
????????????? //
定義驗證碼的字符表
????????????? 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));
????????????? //
在不同的高度上輸出驗證碼的每個字符
????????
????????????? 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)
?????? {
???????????? //
畫背景
????????????? g.setColor(new Color(0xDCDCDC));
????????????? g.fillRect(0, 0, WIDTH, HEIGHT);
????????????? //
隨機產生
120
個干擾點
????????????? 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("
驗證碼處理問題
!");
???????????????????? return;
????????????? }
?????????????
????????????? String savedCode = (String)session.getAttribute("check_code");
????????????? if(savedCode == null)
????????????? {
???????????????????? out.println("
驗證碼處理問題
!");
???????????????????? return;
????????????? }
???????????????????????????
????????????? String checkCode = request.getParameter("check_code");
????????????? if(!savedCode.equals(checkCode))
????????????? {
???????????????????? /*
驗證碼未通過,不從
Session
中清除原來的驗證碼,
????????????????????
以便用戶可以后退回登錄頁面繼續使用原來的驗證碼進行登錄
*/
???????????????????? out.println("
驗證碼無效
!");
???????????????????? return;
????????????? }
????????????? /*
驗證碼檢查通過后,從
Session
中清除原來的驗證碼,
?????????????
以防用戶后退回登錄頁面繼續使用原來的驗證碼進行登錄
*/
????????????? session.removeAttribute("check_code");
????????????? out.println("
驗證碼通過,服務器正在校驗用戶名和密碼
!");
?????? }
}
?
編譯上面的兩個
Java
源
文件,確保編譯后生成的class文件存放在了
<
tomcat
安裝目錄
>\webapps\it315\WEB-INF\classes
目錄中。將
check_code.html
文件保存在
<
tomcat
安裝目錄
>
\webapps\it315
目錄中。
(
2
)在
<
tomcat
安裝目錄
>
\webapps\it315\WEB-INF\web.xml
文件中注冊有關的Servlet,并設置其映射URL。在web.xml文件中的相應位置處增加如下兩段內容:
?????? <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
文件后,重新啟動
Tomcat
。
(
3
)在瀏覽器地址欄中輸入如下地址:
?????? http://localhost:8080/it315/check_code.html
瀏覽器中顯示出如圖
7.25
所示的效果
,然后就可以對驗證碼的功能進行測試了。