驗證碼技術學習



一.常見的驗證碼
1,四位數字,隨機的一數字字符串,最原始的驗證碼,驗證作用幾乎爲零。

2,CSDN網站用戶登錄用的是GIF格式,目前常用的隨機數字圖片驗證碼。圖片上的字符比較中規中矩,驗證作用比上一個好。沒有基本圖形圖像學知識的人,不可破!可惜讀取它的程序,在CSDN使用它的第一天,好像就在論壇裏發佈了,真是可憐!

3,QQ網站用戶登錄用的是PNG格式,圖片用的隨機數字+隨機大寫英文字母,整個構圖有點張揚,每刷新一次,每個字符還會變位置呢!有時候出來的圖片,人眼都識別不了,厲害啊…

4,MS的hotmail申請時候的是BMP格式, 隨機數字+隨機大寫英文字母+隨機干擾像素+隨機位置。

5,Google的Gmail註冊時候的是JPG格式,隨機英文字母+隨機顏色+隨機位置+隨機長度。

6,其他各大論壇的是XBM格式,內容隨機。

二.驗證碼作用分析

驗 證碼起源:因爲攻擊者會使用有害程序註冊大量的 Web 服務帳戶(如 Passport)。攻擊者可以使用這些帳戶爲其他的用戶製造麻煩,如發送垃圾郵件或通過同時反覆登錄多個帳戶來延緩服務的速度。在大多數情況下,自動注 冊程序不能識別此圖片中的字符。簡單的說呢,就是防止攻擊者編寫程序,自動註冊,重複登錄暴力破解密碼。驗證碼技術應運而生。

驗證碼實現 流程:服務器端隨機生成驗證碼字符串,保存在內存中,並寫入圖片,發送給瀏覽器端顯示,瀏覽器端輸入驗證碼圖片上字符,然後提交服務器端,提交的字符和服 務器端保存的該字符比較是否一致。一致就繼續,否則返回提示。攻擊者編寫的robot程序,很難識別驗證碼字符,順利的完成自動註冊,登 錄。。。。。。。。。而用戶可以識別填寫,所以這就實現了阻擋攻擊的作用。而圖片的字符識別,就是看圖片上的干擾強度了。就實際的效果來說,驗證碼只是增 加攻擊者的難度,而不可能完全的防止。


*************************************************************************************************************

在web應用中部署驗證碼(jsp,jsf兩種方式)
在web系統中,驗證碼的應用基本上隨處可見.驗證碼可以防止他人惡意攻擊和垃圾註冊,可以說已成了web開發中必不可少的環節.遺憾的是,驗證碼 在jsp,jsf的組件庫, 至少是一些標準的組件庫中並沒有出現.本文分別介紹如何在jsp和jsf中使用驗證碼和我的一些小經驗,呵呵.

     在jsp中,我們使用apache的taglibs-image:

http://jakarta.apache.org/taglibs/sandbox/doc/image-doc/intro.html

      可以簡便的配置自己的驗證碼.而由於在jsf中,無法和其他jsp標籤庫混用(至少不能和上述標籤庫混用),我們則用Java2D自己繪製驗證碼圖.
 
1. 在jsp中使用taglibs-image部署驗證碼
    taglibs-image可以通過標籤自動將一段文字和背景圖片生成新的圖片,文字的佈局,顏色,字體等等都可以自我定製,因此拿來做驗證碼是非常的簡單。   
<%@ page contentType="text/html; charset=GBK" language="java"%>
<%@ taglib uri="http://jakarta.apache.org/taglibs/image-1.0" prefix="img" %>

<html>
<head>
<title>ImageTagexamples</title>
<metahttp-equiv="Content-Type"content="text/html;charset=iso-8859-1">
</head>

<body>

<%
int num=(int)java.lang.Math.round(java.lang.Math.random()*8999);
String sRand=""+(1000+num);
session.setAttribute("userInfo.authcode",sRand);
%>
<img:image src="/images/auth.jpg" refresh="true">
<img:text text="<%=sRand.substring(0,1)%>" x="18%" y="25%" font="Arial" bold="true" size="16" />
<img:text text="<%=sRand.substring(1,2)%>" x="36%" y="15%" font="TimesNewRoman" bold="true" size="20"/>
<img:text text="<%=sRand.substring(2,3)%>" x="60%" y="20%" font="Arial" bold="true" size="18" />
<img:text text="<%=sRand.substring(3,4)%>" x="77%" y="30%" font="TimesNewRoman" bold="true" size="14" />
</img:image>
</body>
</html>


   其中最開始百分號內的java代碼是爲了生成驗證碼,然後保存在session中.同時驗證碼和背景圖片生成新的驗證圖.用戶根據此圖輸入驗證碼.在服務器方,只用把用戶提交表單中的驗證碼內容取出和session中保存的驗證碼對比,就可以判斷正確性咯
             

2.JSF
   jsf中無法使用上述標籤(會無法渲染出來), 因此,我們自己實現一個生成驗證圖的類,再通過jsf的<h:graphicImage>標籤得以顯示.
  生成驗證碼的java類如下:
package org.myibm.beans;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;

/**
 * 用來自動生成驗證圖和驗證碼,驗證圖是背景圖加上干擾點加上驗證碼
 * 
 * 
@author td
 * 
 
*/

public final class CodeImageGenerator {
    
private final static int DEF_WIDTH = 60;

    
private final static int DEF_HEIGHT = 20;

    
private final static String BASE_PATH = "validate-images";

    
/**
     * 驗證碼
     
*/

    
private String code = "";

    
/**
     * 驗證圖的地址
     
*/

    
private String path;

    
private int width;

    
private int height;

    
private BufferedImage image;

    
/**
     * 驗證圖對應的File對象
     
*/

    
private File target;

    
public CodeImageGenerator() {
        
this(DEF_WIDTH, DEF_HEIGHT);
    }


    
public CodeImageGenerator(int width, int height) {
        
this.width = width;
        
this.height = height;
        generateCodeImage();
    }


    
/**
     * 生成驗證碼和驗證圖
     *
     
*/

    
private void generateCodeImage() {
        
// create the image
        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g 
= image.getGraphics();
        
// set the background color
        g.setColor(new Color(0xDCDCDC));
        g.fillRect(
00, width, height);
        
// draw the border
        g.setColor(Color.black);
        g.drawRect(
00, width - 1, height - 1);
        
// set the font
        g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
        
// create a random instance to generate the codes
        Random random = new Random();
        
// make some confusion
        for (int i = 0; i < 50; i++{
            
int x = random.nextInt(width);
            
int y = random.nextInt(height);
            g.drawOval(x, y, 
00);
        }
 // generate a random code
        for (int i = 0; i < 4; i++{
            String rand 
= String.valueOf(random.nextInt(10));
            code 
+= rand;
            g.drawString(rand, 
13 * i + 616);
        }

        g.dispose();
        
try {
            File dir 
= new File("K:/Tomcat 5.5/webapps/nirvana/validate-images");
            String s 
= new Double(Math.random() * 995596390219L).toString();
            File imgFile 
= new File(dir, s + ".jpeg");
            
if (!imgFile.exists())
                imgFile.createNewFile();
            target 
= imgFile;
            ImageIO.write(image, 
"JPEG", imgFile);
            path 
= "/" + BASE_PATH + "/" + s + ".jpeg";
            System.err.println(path);
        }
 catch (IOException e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    
public BufferedImage getImage() {
        
return image;
    }


    
public String getCode() {
        
if (code == null)
            code 
= "";
        
return code;
    }


    
public static void main(String[] args) throws Exception {
        
// File imgFile = new File("codeImage.jpeg");
        
// CodeImageGenerator cig = new CodeImageGenerator();
        
// ImageIO.write(cig.getImage(), "JPEG", imgFile);
    }


    
public String getPath() {
        
return path;
    }


    
public void setPath(String path) {
        
this.path = path;
    }


    
/**
     * 當這個對象被回收時,同時銷燬其對應的驗證圖
     
*/

    @Override
    
protected void finalize() throws Throwable {
        
// TODO Auto-generated method stub
        
// System.err.println("finalize");
        if (target.exists())
            target.delete();
        
super.finalize();
    }


    
public File getTarget() {
        
return target;
    }


    
public void setTarget(File target) {
        
this.target = target;
    }

}


要說明幾點的事,這個類會把生成的驗證圖放在制定文件夾下,未免得文件越來越多,應該當驗證圖不再使用時將之刪除.所以此類重寫了Object的finalize()方法,當此類被垃圾回收器回收時,同時也刪除其對應的驗證圖.
這樣,就可以利用java的垃圾回收器輕鬆爲我們刪除不用的文件.

另外,在頁面對應的managed-bean中,我們還要添加如何得到驗證碼和驗證圖的方法

private CodeImageGenerator validator;
    
    
private String validate_code;
    
    
public CodeImageGenerator getValidator() {
        
if(validator!=null){
            validator.getTarget().delete();
            validator
=null;
        }

        validator
=new CodeImageGenerator();
        System.out.println(validator.getCode());
        
return validator;
    }


    
public void setValidator(CodeImageGenerator validator) {
        
this.validator = validator;
    }

其中validate-code對應用戶輸入的驗證碼信息
因爲每次刷新頁面都需要得到不同的驗證碼,所以在getValidator()方法時,每次需要返回一個新的CodeImageGenerator.同時,你可能等不及垃圾回收器幫你刪除文件,因此,可以在這裏同時刪除老的驗證圖.

另外,在註冊時我們還需要做一下判斷:
public String register() {
        
// System.out.println("haha");
        if(!validator.getCode().equals(validate_code)){
            FacesMessage message 
= new FacesMessage(
                    FacesMessage.SEVERITY_ERROR, 
"驗證碼錯誤",
                    
"驗證碼錯誤");
            FacesContext.getCurrentInstance().addMessage(
null, message);
            FacesContext fcg 
= FacesContext.getCurrentInstance();
            ((LoginBean) fcg.getApplication().getVariableResolver()
                    .resolveVariable(fcg, 
"loginBean")).setReg(true);
            System.out.println(validator.getCode());
            System.out.println(validate_code);
            
return null;
        }

..................
}

最後,我們需要在頁面中添加對應的標籤


                        <h:outputText value="驗證碼(*):" styleClass="label"></h:outputText>
                        
<h:message for="vcode" styleClass="error"></h:message>

                        
<h:inputText id="vcode" required="true" value="#{myPageBean.validate_code}"></h:inputText>
                        
<h:graphicImage value="#{myPageBean.validator.path}"></h:graphicImage>

這樣, 我們就在jsf中實現了自己的驗證碼部署^_^

****************************************************************************************************************
圖形驗證碼識別技術

項目需要,要在首頁登錄界面添加一個圖形驗證碼,趕時髦吧,網上一搜,特別多,找了幾個,都不太滿意。主要問題是大部分代碼生成的圖片寬度不唯一, 頁面佈局不容易控制,其次是顏色單一,有些又過於抽象,不仔細看很容易弄錯。針對特定的客戶,我只需要“圖片”長寬固定,顏色多樣的數字圖形驗證碼,借鑑 網上的現有代碼,自己操刀完成,以下是效果圖:


原理不復雜,就是把網頁當畫布,運用各色畫筆,在特定區域內畫出數字,然後以特定格式(本例爲PNG格式)發回客戶端,在IE中顯示爲圖片,用於驗證的字符串存於Session中。

主要代碼如下:
// 生成隨機數字字符串
public string GetRandomNumberString(int int_NumberLength)
{
string str_Number = string.Empty;
Random theRandomNumber = new Random();

for (int int_index = 0; int_index < int_NumberLength; int_index++)
str_Number += theRandomNumber.Next(10).ToString();

return str_Number;
}
生成隨機顏色
public Color GetRandomColor()
{
Random RandomNum_First = new Random((int)DateTime.Now.Ticks);
// 對於C#的隨機數,沒什麼好說的
System.Threading.Thread.Sleep(RandomNum_First.Next(50));
Random RandomNum_Sencond = new Random((int)DateTime.Now.Ticks);

// 爲了在白色背景上顯示,儘量生成深色
int int_Red = RandomNum_First.Next(256);
int int_Green = RandomNum_Sencond.Next(256);
int int_Blue = (int_Red + int_Green > 400) ? 0 : 400 - int_Red - int_Green;
int_Blue = (int_Blue > 255) ? 255 : int_Blue;

return Color.FromArgb(int_Red, int_Green, int_Blue);
}
根據驗證字符串生成最終圖象
public void CreateImage(string str_ValidateCode)
{
int int_ImageWidth = str_ValidateCode.Length * 13;
Random newRandom = new Random();
// 圖高20px
Bitmap theBitmap = new Bitmap(int_ImageWidth, 20);
Graphics theGraphics = Graphics.FromImage(theBitmap);
// 白色背景
theGraphics.Clear(Color.White);
// 灰色邊框
theGraphics.DrawRectangle(new Pen(Color.LightGray, 1), 0, 0, int_ImageWidth - 1, 19);

// 10pt的字體
Font theFont = new Font(Arial, 10);

for (int int_index = 0; int_index < str_ValidateCode.Length; int_index++)
{
string str_char = str_ValidateCode.Substring(int_index, 1);
Brush newBrush = new SolidBrush(GetRandomColor());
Point thePos = new Point(int_index * 13 + 1 + newRandom.Next(3), 1 + newRandom.Next(3));
theGraphics.DrawString(str_char, theFont, newBrush, thePos);
}

// 將生成的圖片發回客戶端
MemoryStream ms = new MemoryStream();
theBitmap.Save(ms, ImageFormat.Png);

Response.ClearContent(); //需要輸出圖象信息 要修改HTTP頭
Response.ContentType = image/Png;
Response.BinaryWrite(ms.ToArray());
theGraphics.Dispose();
theBitmap.Dispose();
Response.End();
}

最後在Page_Load中調用以上代碼

private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
// 4位數字的驗證碼
string str_ValidateCode = GetRandomNumberString(4);
// 用於驗證的Session
Session[ValidateCode] = str_ValidateCode;
CreateImage(str_ValidateCode);
}
}
使用的時候在頁面中加入一個Image,將圖片路徑改爲ValidateCode.aspx的相對路徑即可

<img src=ValidateCode.aspx />在需要驗證的地方填入如下代碼:
if (TextBox1.Text == Session[ValidateCode].ToString())
{
TextBox1.Text = 正確!;
}
else
TextBox1.Text = 錯誤!;OK,基本搞定,總結一下:
優點:1. 簡單明瞭,適於簡單運用
2. 界面友好,圖片長寬格式固定
缺點:1. 如果有多個頁面都需要此驗證碼,則會導致Session被其它頁面重寫的情況,可以考慮指定具體Session值爲效驗值
2. 暫時只支持數字,不過更改GetRandomNumberString()中的代碼可以實現指定字符機的隨機字符串
3. 頁面刷新後驗證碼隨之改變

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
jsp教是:JSP彩色驗證碼。

生成有4個隨機數字和雜亂背景的圖片,數字和背景顏色會改變,服務器端刷新(用history.go(-1)也會變)
原型參考ALIBABA  http://china.alibaba.com/member/showimage

產生驗證碼圖片的文件-----image.jsp

<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
Color getRandColor(int fc,int bc){//給定範圍獲得隨機顏色
        Random random = new Random();
        if(fc>255) fc=255;
        if(bc>255) bc=255;
        int r=fc+random.nextInt(bc-fc);
        int g=fc+random.nextInt(bc-fc);
        int b=fc+random.nextInt(bc-fc);
        return new Color(r,g,b);
        }
%>
<%
//設置頁面不緩存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);

// 在內存中創建圖象
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

// 獲取圖形上下文
Graphics g = image.getGraphics();

//生成隨機類
Random random = new Random();

// 設定背景色
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);

//設定字體
g.setFont(new Font("Times New Roman",Font.PLAIN,18));

//畫邊框
//g.setColor(new Color());
//g.drawRect(0,0,width-1,height-1);


// 隨機產生155條幹擾線,使圖象中的認證碼不易被其它程序探測到
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
 int x = random.nextInt(width);
 int y = random.nextInt(height);
        int xl = random.nextInt(12);
        int yl = random.nextInt(12);
 g.drawLine(x,y,x+xl,y+yl);
}

// 取隨機產生的認證碼(4位數字)
String sRand="";
for (int i=0;i<4;i++){
    String rand=String.valueOf(random.nextInt(10));
    sRand+=rand;
    // 將認證碼顯示到圖象中
    g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110))); //調用函數出來的顏色相同,可能是因爲種子太接近,所以只能直接生成
    g.drawString(rand,13*i+6,16);
}

// 將認證碼存入SESSION
session.setAttribute("rand",sRand);


// 圖象生效
g.dispose();

// 輸出圖象到頁面
ImageIO.write(image, "JPEG", response.getOutputStream());


%>

---------------使用驗證碼圖片的文件---------a.jsp------------------------------------

<%@ page contentType="text/html;charset=gb2312" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>認證碼輸入頁面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<form method=post action="check.jsp">
<table>
<tr>
<td align=left>系統產生的認證碼:</td>
<td><img border=0 src="image.jsp"></td>
</tr>
<tr>
<td align=left>輸入上面的認證碼:</td>
<td><input type=text name=rand maxlength=4 value=""></td>
</tr>
<tr>
<td colspan=2 align=center><input type=submit value="提交檢測"></td>
</tr>
</form>
</body>
</html>

-----------------驗證的頁面----------check.jsp

<%@ page contentType="text/html; charset=gb2312" language="java" import="java.

<html>
<head>
<title>認證碼驗證頁面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>

<body>
<%
 String rand = (String)session.getAttribute("rand");
 String input = request.getParameter("rand");
%>
系統產生的認證碼爲: <%= rand %><br>
您輸入的認證碼爲: <%= input %><br>
<br>
<%
  if (rand.equals(input)) {
%>
<font color=green>輸入相同,認證成功!</font>
<%
  } else {
%>
<font color=red>輸入不同,認證失敗!</font>
<%
  }
%>
</body>
</html>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章