191124_01 基於位置驗證的圖形驗證碼

基於位置驗證的圖形驗證碼

作者:邵發
官網:http://afanihao.cn/java

本文是Java學習指南系列教程的官方配套文檔。內容介紹一種基於位置的驗證碼的實現,附演示說明和項目源碼。

 

1.  基於位置的圖形驗證碼

下面演示一種基於位置的驗證碼,示意圖如下。

( 項目演示http://127.0.0.1:8080/demo/test  )

和所有的驗證碼一樣,此驗證碼依賴人的視覺。用戶需要用鼠標文字在圖片中的出現位置。當用戶點中正確時,則驗證通過。否則驗證失敗。

 

2.  驗證碼圖片的生成

總體上,後臺需要隨機的生成一張圖片,將等識別目標和干擾目標都繪製在圖片中。此圖片在前端頁面中顯示,前端將用戶點擊的位置座標(clickX, clickY)發給後臺,後臺校驗點擊的位置是否正確。

首先,後臺隨機挑選一些文字用於顯示,其中一個文字爲識別目標,其他文字爲干擾文字。例如,drawChars = "羊馬猴",選取“猴”爲識別目標。

public void random()
{
	Random rand = new Random();
	drawChars = "";
	while(true)
	{
		int idx = rand.nextInt(charBase.length());
		char ch = this.charBase.charAt(idx);
		if( drawChars.indexOf(ch) <0)
		{
			drawChars += ch;
			if(drawChars.length()>=3) break;
		}
	}

	// 
	targetIndex = rand.nextInt(drawChars.length());
	targetChar = drawChars.charAt(targetIndex);
}

然後,將這些文字繪製爲一個PNG圖片。使用Java Swing中的繪製技術,將文字以傾斜方式繪製。

FontMetrics fm = g2d.getFontMetrics(g2d.getFont());
int fontSize = fm.getHeight(); // 字高
int textWidth = fm.stringWidth(theChar + "");
int descent = fm.getDescent(); // bottom->baseline 的高度

AffineTransform saveAT = g2d.getTransform();
g2d.translate((double)cx, (double)cy);
g2d.rotate(angle);
g2d.setPaint(fontColor);
g2d.drawString(theChar + "", -textWidth/2, fontSize/2-descent);
g2d.setTransform(saveAT);

其中,cx, cy是文字隨機顯示的位置,angle是文字的旋轉角度, fontColor是一個隨機的顏色。最終繪製出來的圖片,各個要素都是隨機的。

 

3.  驗證流程

3.1 /verify/refresh.do

前端首先應該調用這個接口,來刷新驗證碼。

@RequestMapping("/verify/refresh.do")
public Object refresh( HttpSession session)
{
	// 產生新的隨機驗證碼,放在Session裏
	VerifyPng verify = new VerifyPng();
	verify.random();
	session.setAttribute("verify", verify);

	Map<String,Object> data = new HashMap<>();
	data.put("targetChar", "" + verify.targetChar );
	return new AfRestData(data);
}

生成的驗證信息放在Session中,verify對象中包含了識別目標出現的位置。

3.2  /verify/show

前端調用此接口顯示圖片。注意,這個是一個僞靜態URL,直接作一張圖片URL顯示。

@GetMapping("/verify/show")
public void show ( HttpSession session
		, HttpServletResponse response) throws Exception
{
	// 生成PNG發給客戶端
	response.setContentType("image/png");
	response.setHeader("Cache-Control", "no-cache");

	VerifyPng verify = (VerifyPng)session.getAttribute("verify");
	verify.toPNG(response.getOutputStream());
}

前端需要把這張圖片呈現到用戶頁面裏顯示。

 

3.3 /verify/check.do

用戶根據提示,肉眼觀察圖片,鼠標點擊識別目標文字。前端JS響應鼠標點擊動作,將鼠標點擊的位置發給後臺,由後臺校驗。

@PostMapping("/verify/check.do")
public Object check ( HttpSession session
		, @RequestBody JSONObject jreq) throws Exception
{
	VerifyPng v = (VerifyPng)session.getAttribute("verify");
	session.removeAttribute("verify");
	if(v== null)
		return new AfRestError(-8, "驗證碼校驗出錯");

	int clickX = jreq.getIntValue("clickX");
	int clickY = jreq.getIntValue("clickY");

	。。。

}

 

4.  前端顯示

開始時,前端需要調用 /verify/refresh.do 來刷新驗碼圖片。

vf.refresh = function(){
	var req = {};
	Af.rest('[[@{/verify/refresh.do}]]' , req, function(data){
		vf.status = 0;
		vf.repaint();
		$('.targetChar').text( data.targetChar);
	})
}

刷新完成後,將新的驗證碼顯示到界面上,

vf.repaint = function(){
	var png = new Image();
	png.src = '[[@{/verify/show}]]?t=' + new Date().getTime() ;

	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');
	var w = canvas.width;
	var h = canvas.height;

	png.onload = function(){
		ctx.drawImage( png, 0, 0);
	}
}

當用戶點擊Canvas時,調用 /verify/check.do 進行驗證,

vf.check = function(clickX, clickY){
	var req ={};
	req.clickX = clickX;
	req.clickY = clickY;
	Af.rest('[[@{/verify/check.do}]]' , req, function(data){
		vf.status = 100;
		vf.showResult(true, clickX, clickY);
	},
	function(error,reason){
		vf.status = -1;
		vf.showResult(false, clickX, clickY);
	})

}

當驗證成功後時,在 Canvas上繪製一個OK圖標。如果失敗,繪製一個出錯的圖標。

示例中,驗證圖片是240x80像素,每次顯示3個文字,可以根據實際項目中的需要自行調試。以上演示所用的項目源碼和JAR包在此處可以獲取

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