仿12306的圖片驗證碼

来源:http://www.cnblogs.com/qldhlbs/archive/2016/04/13/5386679.html
-Advertisement-
Play Games

由於要做一個新項目,所以打算做一個簡單的圖片驗證碼。 先說說思路吧:在服務端,從一個文件夾裡面找出8張圖片,再把8張圖片合併成一張大圖,在8個小圖裡面隨機生成一個要用戶驗證的圖片分類,如小狗、啤酒等。在前端,訪問這個頁面時,把圖片載入上去,用戶在圖片上選擇提示所需要的圖片,當用戶點登陸時,根據用戶選 ...


由於要做一個新項目,所以打算做一個簡單的圖片驗證碼。

先說說思路吧:在服務端,從一個文件夾裡面找出8張圖片,再把8張圖片合併成一張大圖,在8個小圖裡面隨機生成一個要用戶驗證的圖片分類,如小狗、啤酒等。在前端,訪問這個頁面時,把圖片載入上去,用戶在圖片上選擇提示所需要的圖片,當用戶點登陸時,根據用戶選擇的所有坐標判斷所選的圖片是不是實際上的驗證圖片。

先放兩張效果圖:

為了讓文件查找比較簡單,在圖片文件結構上可以這樣:

這樣方便生成用戶要選擇的Key圖片,和取出8張小圖合併成大圖。

上代碼:這是選擇8張圖片,並且在每張圖片選取時用遞歸保證選取的圖片不會重覆。

        //選取8個圖片
	public static List<Object> getEightImages() {
		//保存取到的每一個圖片的path,保證圖片不會重覆
		List<String> paths = new ArrayList<String>();
		
		File[] finalImages = new File[8];
		List<Object> object = new ArrayList<Object>();
		
		//保存tips
		String[] tips = new String[8];
		
		for (int i = 0; i < 8; i++) {
			//獲取隨機的二級目錄
			int dirIndex = getRandom(secondaryDirNumbers);
			File secondaryDir = getFiles()[dirIndex];
			
			//隨機到的文件夾名稱保存到tips中
			tips[i] = secondaryDir.getName();
			
			//獲取二級圖片目錄下的文件
			File[] images = secondaryDir.listFiles();
			
			int imageIndex = getRandom(imageRandomIndex);
			File image = images[imageIndex];
			
			//圖片去重
			image = dropSameImage(image, paths, tips, i);		
			
			paths.add(image.getPath());

			finalImages[i] = image;
			
		}
		object.add(finalImages);
		object.add(tips);
		return object;
	}    

在生成這8張圖片中,用一個數組保存所有的文件分類。在這個分類裡面可以用隨機數選取一個分類做為Key分類,就是用戶要選擇的所有圖片。由於數組是有序的,可以遍曆數組中的元素,獲取每個key分類圖片的位置,方便在用戶驗證時,進行匹配。

//獲取位置,返回的是第幾個圖片,而不是下標,從1開始,集合第一個元素為tip
	public static List<Object> getLocation(String[] tips) {
		List<Object> locations = new ArrayList<Object>();
	
		//獲取Key分類
		String tip = getTip(tips);
		locations.add(tip);
		
		int length = tips.length;
		for (int i = 0; i < length; i++) {
			if (tip.equals(tips[i])) {

				locations.add(i+1);
			}
		}
		return locations;
	}

 

選取了8張圖片後,接下來就是合併圖片。合併圖片可以用到BufferedImage這個類的方法:setRGB()這個方法如果不明白可以看下api文檔,上面有詳細的說明。

public static void mergeImage(File[] finalImages, HttpServletResponse response) throws IOException {
                
        //讀取圖片
        BufferedImage mergeImage = new BufferedImage(800, 400, BufferedImage.TYPE_INT_BGR);
        
        for (int i = 0; i < 8; i++) {
            File image = finalImages[i];
            
            BufferedImage bufferedImage = ImageIO.read(image);
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            //從圖片中讀取RGB
            int[] imageBytes = new int[width*height];
            imageBytes = bufferedImage.getRGB(0, 0, width, height, imageBytes, 0, width);
            if ( i < 4) {
                mergeImage.setRGB(i*200, 0, width, height, imageBytes, 0, width);
            } else {
                mergeImage.setRGB((i -4 )*200, 200, width, height, imageBytes, 0, width);
            }            
            
        }

     
        ImageIO.write(mergeImage, "jpg", response.getOutputStream());
        //ImageIO.write(mergeImage, "jpg", destImage);
    }

 

 

  在controller層中,先把key分類保存到session中,為用戶選擇圖片分類做提示和圖片驗證做判斷。然後把圖片流輸出到response中,就可以生成驗證圖片了。

  

        response.setContentType("image/jpeg");  
        response.setHeader("Pragma", "No-cache");  
        response.setHeader("Cache-Control", "no-cache");  
        response.setDateHeader("Expires", 0);
        
        List<Object> object = ImageSelectedHelper.getEightImages();
        File[] finalImages = (File[]) object.get(0);
        
        String[] tips = (String[]) object.get(1);
        //所有key的圖片位置,即用戶必須要選的圖片
        List<Object> locations = ImageSelectedHelper.getLocation(tips);
        
        String tip = locations.get(0).toString();
        System.out.println(tip);
        session.setAttribute("tip", tip);
        locations.remove(0);
        
        int length = locations.size();
        for (int i = 0; i < length; i++) {
            System.out.println("實際Key圖片位置:" + locations.get(i));
        }
session.setAttribute("locations", locations);
ImageMerge.mergeImage(finalImages, response);

 

  在jsp中,為用戶的點擊生成小圖片標記。當用戶點圖片擊時,在父div上添加一個子div標簽,並且把他定位為relative, 並且設置zIndex,然後再這個div上添加一個img標簽,定位為absolute。在用戶的點擊時,可以獲取點擊事件,根據點擊事件獲取點擊坐標,然後減去父div的坐標,就可以獲取相對坐標。可以根據自己的喜好定坐標原點,這裡的坐標原點是第8個圖片的右下角。

  

  <div>
        <div id="base">
            <img src="<%=request.getContextPath()%>/identify" style="width: 300px; height: 150px;" onclick="clickImg(event)" id="bigPicture">
        </div>
        
    </div>

function clickImg(e) { var baseDiv = document.getElementById("base");    var topValue = 0; var leftValue = 0; var obj = baseDiv; while (obj) { leftValue += obj.offsetLeft; topValue +=obj.offsetTop; obj = obj.offsetParent; } //解決firefox獲取不到點擊事件的問題 var clickEvent = e ? e : (window.event ? window.event : null); var clickLeft = clickEvent.clientX + document.body.scrollLeft - document.body.clientLeft - 10; var clickTop = clickEvent.clientY + document.body.scrollTop - document.body.clientTop - 10; var divId = "img_" + index++; var divEle = document.createElement("div"); divEle.setAttribute("id", divId); divEle.style.position = "relative"; divEle.style.zIndex = index; divEle.style.width = "20px"; divEle.style.height = "20px"; divEle.style.display = "inline"; divEle.style.top = clickTop - topValue - 150 + 10 + "px"; divEle.style.left = clickLeft - leftValue - 300 + "px"; divEle.setAttribute("onclick", "remove('" + divId + "')"); baseDiv.appendChild(divEle); var imgEle = document.createElement("img"); imgEle.src = "<%=request.getContextPath()%>/resources/timo.png"; imgEle.style.width = "20px"; imgEle.style.height = "20px"; imgEle.style.top = "0px"; imgEle.style.left = "0px"; imgEle.style.position = "absolute"; imgEle.style.zIndex = index; divEle.appendChild(imgEle); }

用戶選擇登錄後,伺服器端根據用戶的選擇坐標進行判斷

public List<Integer> isPass(String result) {
		
		String[] xyLocations = result.split(",");
		//保存用戶選擇的坐標落在哪些圖片上
		List<Integer> list = new ArrayList<Integer>();
		//每一組坐標
		System.out.println("用戶選擇圖片數:"+xyLocations.length);
		for (String xyLocation : xyLocations) {
			String[] xy = xyLocation.split("\\|\\|");
			int x = Integer.parseInt(xy[0]);
			int y = Integer.parseInt(xy[1]);
			
			//8,4圖片區間
			if ( x > -75 && x <= 0) {

				if ( y > -75 && y <= 0) {		//8號
					list.add(8);
	
				} else if ( y >= -150 && y <= -75 ) {		//4號
					list.add(4);
				}
			} else if ( x > -150 && x <= -75) {		//7,3圖片區間
				
				if ( y > -75 && y <= 0) {		//7號
					list.add(7);
	
				} else if ( y >= -150 && y <= -75 ) {		//3號
					list.add(3);
				}
			} else if ( x > -225 && x <= -150) {		//6,2圖片區間
				
				if ( y > -75 && y <= 0) {		//6號
					list.add(6);
	
				} else if ( y >= -150 && y <= -75 ) {		//2號
					list.add(2);
				}
				
			} else if ( x >= -300 && x <= -225) {		//5,1圖片區間
				
				if ( y > -75 && y <= 0) {		//5號
					list.add(5);
	
				} else if ( y >= -150 && y <= -75 ) {		//1號
					list.add(1);
				}
			} else {
				return null;
			}
		}
		return list;
	}

 

 

刷新生成新的圖片,由於ajax不支持二進位流,可以自己用原生的xmlHttpRequest對象加html5的blob來完成。

	function refresh() {
        var url = "<%=request.getContextPath()%>/identify";
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = "blob";
        xhr.onload = function() {
            if (this.status == 200) {
                var blob = this.response;                
                //載入成功後釋放blob
                bigPicture.onload = function(e) {
                    window.URL.revokeObjectURL(bigPicture.src); 
                };
                bigPicture.src = window.URL.createObjectURL(blob);
            }
        }
        xhr.send();

 驗證碼整體代碼完成了,還有有一些細節要處理。由於圖片容易被百度識圖,要對生成的圖片做模糊處理,暫時還沒想到什麼好的辦法~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • package com.itcast.day3; import java.io.ObjectInputStream.GetField; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; ... ...
  • 為什麼想起來做這個功能?是因為前段時間在做一個淘客網站的時候,想到是否能抓取到淘寶商品的買家秀呢?經過一番折騰發現,淘寶商品用戶評價信息是通過Ajax來調取的,通過嗅探網址發現,評論數據的請求介面是: 其實上面很多參數也很容易理解,itemId是商品的ID,currentPage是當前頁,pictu ...
  • 唉,本人學習進度緩慢,但依然會堅持不懈!有感興趣的朋友可以在下麵留言。 源代碼獻上: /* * 畫出我的坦克,使他可以上下左右移動 */package com.test4; import javax.swing.*;import java.awt.*;import java.awt.event.*; ...
  • 1、建立三個consul節點(一個server+兩個client) 具體的過程見http://www.cnblogs.com/java-zhao/p/5375132.html 1)在終端下啟動vagrant vagrant up(Vagrantfile還是建立兩個節點,如下) 1 # -*- mod ...
  • 1,保留兩位小數 ...
  • 一些平時常見的錯誤及解決辦法,我 是新手,每次遇到的錯誤都記錄了下來。 1. 404錯誤 description The requested resource (/Struts2_0100_Introduction/hello.action) is not available. 先檢查Manager ...
  • 想著給框架加些功能 首先想到的是生成測試報告 這裡就涉及到了生成什麼格式的文件 我這邊就準備生成 xml 格式的文件 自己先學習了整理了下 代碼如下: 生成的xml 文件內容如下: ...
  • OCR(Optical Character Recognition):光學字元識別,是指對圖片文件中的文字進行分析識別,獲取的過程。 Tesseract:開源的OCR識別引擎,初期Tesseract引擎由HP實驗室研發,後來貢獻給了開源軟體業,後經由Google進行改進,消除bug,優化,重新發佈。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...