2010年11月13日 星期六

JSP 教學 - 圖文驗証碼製作

概述:在登入或是註冊的時候加上驗證碼,可以防止有心人批量的註冊或登入或是到取別人的
帳密。

圖形驗證碼的製作以 Java 2D 為基礎,透過 Servlet 動態產生驗證碼

這邊作法有兩種,一種是已經事先產生好大量的驗證碼再去隨機取一張,另一種是如上述的方法

動態產生一張圖形驗證碼,這樣是比較安全的狀況,如果把驗證的圖放在某目錄下又沒加以保護

很容易讓人直接以URL取得你的所有圖檔,

以 Xuite 為例就是如此,在我這製作專題的過程中就是碰到類似問題幸好.....Xuite 不是動態產生,不然專題早掛摟....

首先先配置一個 Servlet 吧,以下為 SignalPage.java
package fsc.regLogIdent.controller;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
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;
public class SignalPage extends HttpServlet
{
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
    {
        HttpSession session = request.getSession();
        response.setContentType("image/jpeg");  //設定回應的型態為 image/jpeg
        int width = 86;    //圖形的寬度
        int height = 25;     //圖形的高度
        Random random = new Random();    
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);     //建立圖像
        Graphics g = image.getGraphics();
        Graphics2D g2d = (Graphics2D)g;
        Font f = new Font("黑體", Font.BOLD, 17);  //設定字型的屬性
        g.setColor(Color.LIGHT_GRAY);     //圖像背景為亮灰色
        g.fillRect(0, 0, width, height);     //將背景繪上
        g.setFont(f);     //設定字型
        g.setColor(Color.BLACK);    //再將背景設定為黑色
        //以下的迴圈會產生 100 條的干擾線
        for(int i=0;i<100;i++)
        {
            //產生兩組作標
            int x1 = random.nextInt(width-1);  
            int y1 = random.nextInt(height-1);
            int x2 = random.nextInt(6) + 1;
            int y2 = random.nextInt(12) + 1;
            //建立干擾線的輪廓 bsk
            BasicStroke bsk = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
            Line2D line = new Line2D.Double(x1, y1, x1+x2, y1+y2);
            g2d.setStroke(bsk);
            g2d.draw(line);
        }
        String code = "";  //驗證碼
        for(int i=0;i<4;i++)   //產生四個隨機的英文或是數字
        {
            switch(random.nextInt(2))
            {
                case 0:  //英文字母
                    int r = random.nextInt(26) + 65;
                    code = code + String.valueOf((char)r);
                    break;
                case 1:  //數字
                    int n = random.nextInt(10) + 48;
                    code = code + String.valueOf((char)n);
            }
            g.setColor(Color.YELLOW);    //設定字型顏色
            Graphics2D g2dAT = (Graphics2D)g;
            AffineTransform tran = new AffineTransform();
            tran.scale(1.1, 1.3);   //縮放
            tran.rotate(random.nextInt(10)*3.14/180, 28, 14); //旋轉
            g2dAT.setTransform(tran);
            g.drawString(code, 18, 14);   //將驗證碼繪上
        }
        session.setAttribute("code", code);  //附加到 session 屬性中, 以便後續驗證所需
        g.dispose();
        //這裡要用 ServletOutputStream, 不要用 OutputStream 
        ServletOutputStream  os = response.getOutputStream();   
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
        encoder.encode(image);
    }
}

接下來看一下網頁吧 這邊只截取關鍵部份 SignalPage.jsp

<div style="padding-top: 5px;">
        Identify : <input id="inputCode" type="text" size="5" onClick="showCode()" onkeyup="checkCode()" onmousemove="checkCode()" /> , Please click here for geting the Identify code.<br />
        <input id="send" type="submit" value="Send" disabled="true" />
        <p id="errorMsg" style="visibility: hidden;"></p>
</div>
<div id="show"></div>

需要使用者對輸入驗證碼的欄位點擊才會產生驗證碼所以以下的 div 初始值是 hidden
針對欄位的處理 當使用者放開任何案件或是滑鼠移動時
會用AJAX的方法檢查驗證碼是否正確


<div id="code" style="visibility: hidden;position: absolute;margin-left: 400px; width: 100px;" align="right">
        <img id="identifyIMG" src="http://localhost:8084/regLogIdent/SignalPage"  />
        <a href="#" onClick="reloadIdentify()">換一個</a>
</div>


以上圖片的 src 就直接呼叫動態 Servlet 以產生圖形,接下來如果使用者看不清楚呢?

那就換一個吧,點擊 a link 會呼叫 javascript 的 reloadIdentify function
function reloadIdentify()
{
        document.getElementById('inputCode').value = "";     //將輸入欄位清空
        document.getElementById('inputCode').focus();
        document.getElementById('identifyIMG').src = document.getElementById('identifyIMG').src + "?reload=" + new Date().getTime();       //重新設定 src
}


接下來用 AJAX 去驗證使用者輸入的驗證法是否正確  checkCode function

function checkCode()
{
    if((document.getElementById('inputCode').value).toString().length >= 4){
        var myXmlHttp = new XMLHttpRequest();
        myXmlHttp.overrideMimeType("text/html");
        var code = (document.getElementById('inputCode').value).toString();
        myXmlHttp.onreadystatechange = function(){
            if(myXmlHttp.readyState == 4){
                if(myXmlHttp.state == 200){}
                else{
                    if(myXmlHttp.responseText != 1){   // 不等於 1 代表驗證碼輸入錯誤
                        document.getElementById('errorMsg').innerHTML = myXmlHttp.responseText;
                    }else{
                         //隱藏錯誤訊息
                         document.getElementById('errorMsg').style.valueOf().visibility = "hidden";   
                          //移除 submit 的 disable
                         document.getElementById('send').removeAttribute('disabled');  
                    }
                }
            }
        }
        //checkCode 參數為使用者輸入的驗證碼  method 參數 為要使用的服務, 1 代表要效正驗證碼
        myXmlHttp.open('GET', 'http://localhost:8084/regLogIdent/checkIndentify.jsp?checkCode=' + code + '&method=1', true);
        myXmlHttp.send(null);
    }else{   //如果驗證碼長度小於四, 則顯示說長度小於四
       if(document.getElementById('errorMsg').style.valueOf().visibility == "visible"){
          document.getElementById('errorMsg').innerHTML = "The code is too short";
       }
    }
}

以上的 AJAX 會呼叫 http://localhost:8084/regLogIdent/checkIndentify.jsp  並夾帶兩個參數


此JSP純粹只是用來驗證驗證碼或是一些其他的動作,以下為該JSP 的程式

<%

    response.setContentType("text/html");
        if(request.getParameter("method").equals("1")){    //method 為 1 代表要驗證驗證碼
            if(request.getParameter("checkCode") != null){      //從 session 屬性取出 servlet 產生的驗證碼
                if(request.getParameter("checkCode").equals(session.getAttribute("code"))){
                    out.println("1");    //成功驗證
                }else{
                    out.println("error code !!");   //失敗
                }
            }else{
                   out.println("Invaild Way !!");
            }
        }else if(request.getParameter("method").equals("2")){
                //其他服務動作
        }

%>

沒有留言:

張貼留言