(附代码)手写滑动验证码,完整代码开放
目錄
一、概述
二、原理分析? ??
三、代碼實現
四、測試驗證
五、總結
一、概述
滑動驗證碼在很多網站流行,一方面對用戶體驗來說,比較新穎,操作簡單,另一方面相對圖形驗證碼來說,安全性并沒有很大的降低。當然到目前為止,沒有絕對的安全驗證,只是不斷增加攻擊者的繞過成本。
二、原理分析? ??
接下來分析下滑動驗證碼的核心流程:
????? ? 這里單純校驗用戶滑動距離是最基本的校驗,出于更高的安全考慮,可能還會考慮用戶滑動的整個軌跡,用戶在當前頁面的訪問行為等。這些可以很復雜,甚至借助到用戶行為數據分析模型,最終的目標都是增加非法的模擬和繞過的難度。這些有機會可以再歸納總結常用到的方法,本文重點集中在如何基于 Java 來一步步實現滑動驗證碼的生成。
? ? 可以看到,滑動圖形驗證碼,重要有兩個圖片組成,摳塊和帶有摳塊陰影的原圖,這里面有兩個重要特性保證被暴力和 (破) 諧 (解) 的難度:摳塊的形狀隨機和摳塊所在原圖的位置隨機。這樣就可以在有限的圖集中制造出隨機的、無規律可尋的摳圖和原圖的配對。
三、代碼實現
? ? 用代碼如何從一張大圖中摳出一個有特定隨機形狀的小圖呢?
? ? 第一步,先確定一個摳出圖的輪廓,方便后續真正開始執行圖片處理操作
? ? 圖片是有像素組成,每個像素點對應一種顏色,顏色可以用 RGB 形式表示,外加一個透明度,把一張圖理解成一個平面圖形,左上角為原點,向右 x 軸,向下 y 軸,一個坐標值對應該位置像素點的顏色,這樣就可以把一張圖轉換成一個二維數組。基于這個考慮,輪廓也用二維數組來表示,輪廓內元素值為 1,輪廓外元素值對應 0。
? ? 這時候就要想這個輪廓形狀怎么生成了。有坐標系、有矩形、有圓形,沒錯,用到數學的圖形函數。典型用到一個圓的函數方程和矩形的邊線的函數,類似:
(x-a)2+(y-b)2=r2 中,有三個參數 a、b、r,即圓心坐標為 (a,b),半徑 r。這些將摳圖放在上文描述的坐標系上很容易就圖算出來具體的值。
示例代碼如下:
private int[][] getBlockData() {int[][] data = new int[targetLength][targetWidth];double x2 = targetLength-circleR-2;//隨機生成圓的位置double h1 = circleR + Math.random() * (targetWidth-3*circleR-r1);double po = circleR*circleR;double xbegin = targetLength-circleR-r1;double ybegin = targetWidth-circleR-r1;for (int i = 0; i < targetLength; i++) {for (int j = 0; j < targetWidth; j++) {//右邊○double d3 = Math.pow(i - x2,2) + Math.pow(j - h1,2);if (d1 <= po|| (j >= ybegin && d2 >= po)|| (i >= xbegin && d3 >= po)) {data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}? ? 第二步,有這個輪廓后就可以依據這個二維數組的值來判定摳圖并在原圖上摳圖位置處加陰影。
? ? 操作如下:
private void cutByTemplate(BufferedImage oriImage,BufferedImage targetImage, int[][] templateImage, int x,int y){for (int i = 0; i < targetLength; i++) {for (int j = 0; j < targetWidth; j++) {int rgb = templateImage[i][j];// 原圖中對應位置變色處理int rgb_ori = oriImage.getRGB(x + i, y + j);if (rgb == 1) {//摳圖上復制對應顏色值targetImage.setRGB(i, y + j, rgb_ori);int r = (0xff & rgb_ori);int g = (0xff & (rgb_ori >> 8));int b = (0xff & (rgb_ori >> 16)));rgb_ori = r + (g << 8) + (b << 16) + (200 << 24);//原圖對應位置顏色變化oriImage.setRGB(x + i, y + j, rgb_ori);} }}}? ? 經過前面兩步后,就得到了摳圖和帶摳圖陰影的原圖。為增加混淆和提高網絡加載效果,還需要對圖片做進一步處理。一般有兩件事需要做,一對圖片做模糊處理增加機器識別難度,二做適當同質量壓縮。模糊處理很容易想到高斯模糊,原理很好理解,大家可以去 google 了解下。具體到 Java 里面的實現,有很多版本,現在不借助任何第三方 jar,提供一個示例:
public static ConvolveOp getGaussianBlurFilter(int radius,boolean horizontal) {if (radius < 1) {throw new IllegalArgumentException("Radius must be >= 1");}int size = radius * 2 + 1;float[] data = new float[size];float sigma = radius / 3.0f;float twoSigmaSquare = 2.0f * sigma * sigma;float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);float total = 0.0f;for (int i = -radius; i <= radius; i++) {float distance = i * i;int index = i + radius;data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;total += data[index];}for (int i = 0; i < data.length; i++) {data[i] /= total;} Kernel kernel = null;if (horizontal) {kernel = new Kernel(size, 1, data);} else {kernel = new Kernel(1, size, data);}return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);}public static void simpleBlur(BufferedImage src,BufferedImage dest) {BufferedImageOp op = getGaussianBlurFilter(2,false);op.filter(src, dest);}? ? 經測試模糊效果很不錯。另外是圖片壓縮,也不借助任何第三方工具,做同質壓縮。
public static byte[] fromBufferedImage2(BufferedImage img,String imagType) throws IOException {bos.reset();// 得到指定Format圖片的writerIterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(imagType);ImageWriter writer = (ImageWriter) iter.next(); // 得到指定writer的輸出參數設置(ImageWriteParam )ImageWriteParam iwp = writer.getDefaultWriteParam();iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // 設置可否壓縮iwp.setCompressionQuality(1f); // 設置壓縮質量參數iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);ColorModel colorModel = ColorModel.getRGBdefault();// 指定壓縮時使用的色彩模式iwp.setDestinationType(new javax.imageio.ImageTypeSpecifier(colorModel,colorModel.createCompatibleSampleModel(16, 16)));writer.setOutput(ImageIO.createImageOutputStream(bos));IIOImage iIamge = new IIOImage(img, null, null);writer.write(null, iIamge, iwp);byte[] d = bos.toByteArray();return d;}? ? 至此,滑動驗證碼核心的代碼處理流程已全部結束,這里面有很多細節可以不斷打磨優化,讓滑動體驗可以變得更好。希望可以幫助到某些準備自己構建滑動驗證碼的同學。
四、測試驗證
簡單測試代碼如下:固定輸入距離值200,檢驗結果。
public static void main(String[] args) throws InterruptedException {log.info("Go");SwipeCaptchaService scs = new SwipeCaptchaServiceImpl();String channel = "user-login";String uid = "u123456";UserCaptcha uc = scs.getCaptcha(channel, uid, System.currentTimeMillis());Thread.sleep(1000); // Thread.sleep(5000);CaptchaResult cr = scs.verifyCaptcha(channel, uid, uc.getUuid(), "200");log.info("驗證結果,code:{},msg:{}",cr.getCode(),cr.getMsg());cr = scs.verifyCaptcha(channel, uid, uc.getUuid(), "200");log.info("重復驗證結果,code:{},msg:{}",cr.getCode(),cr.getMsg());}為方便本地測試,代碼中將誤差值放大到100px,驗證有效期為5s,一方面增大成功率,一方面模擬有效期。
1. 驗證不通過場景,模擬滑動位置不匹配:輸入200距離,誤差是113,超過誤差閾值100
23:37:24 ?[ com.echx.captcha.service.SwipeCaptchaServiceImpl ] - [ INFO ] ?Generated captcha distance:313, uid:u123456, uuid:714867ee-2b91-484e-aaa4-2b0f6e4a11d1
23:37:25 ?[ com.echx.captcha.bean.CachedCaptcha ] - [ INFO ] ?uuid:714867ee-2b91-484e-aaa4-2b0f6e4a11d1,滑動誤差值:113
23:37:25 ?[ com.echx.captcha.main.AppMain ] - [ INFO ] ?驗證結果,code:-1,msg:驗證失敗
23:37:25 ?[ com.echx.captcha.main.AppMain ] - [ INFO ] ?重復驗證結果,code:-1,msg:驗證失敗
2. 驗證通過場景,滑動距離準確,不能二次驗證:
23:41:06 ?[ com.echx.captcha.service.SwipeCaptchaServiceImpl ] - [ INFO ] ?Generated captcha distance:202, uid:u123456, uuid:75baf690-8e66-4115-843c-dc29ca208a4b
23:41:07 ?[ com.echx.captcha.bean.CachedCaptcha ] - [ INFO ] ?uuid:75baf690-8e66-4115-843c-dc29ca208a4b,滑動誤差值:2
23:41:07 ?[ com.echx.captcha.main.AppMain ] - [ INFO ] ?驗證結果,code:0,msg:驗證通過
23:41:07 ?[ com.echx.captcha.main.AppMain ] - [ INFO ] ?重復驗證結果,code:-1,msg:驗證失敗
3. 驗證不通過場景,Thread.sleep(5000),??過期:
23:49:44 ?[ com.echx.captcha.service.SwipeCaptchaServiceImpl ] - [ INFO ] ?Generated captcha distance:156, uid:u123456, uuid:ace36c66-5592-4669-8544-b8230551fb68
23:49:50 ?[ com.echx.captcha.bean.CachedCaptcha ] - [ INFO ] ?uuid:ace36c66-5592-4669-8544-b8230551fb68,滑動誤差值:44
23:49:50 ?[ com.echx.captcha.service.SwipeCaptchaServiceImpl ] - [ WARN ] ?驗證過期:user-login,u123456,ace36c66-5592-4669-8544-b8230551fb68,200,1652197790957
23:49:50 ?[ com.echx.captcha.main.AppMain ] - [ INFO ] ?驗證結果,code:-2,msg:驗證過期
23:49:50 ?[ com.echx.captcha.main.AppMain ] - [ INFO ] ?重復驗證結果,code:-1,msg:驗證失敗
五、總結
以上代碼實現都非常的精煉,一方面為了保證性能,另一方面好理解。完整代碼在GitHub-techingon/swipe-captcha中開放,如遇疑問,可留言交流。經測試,該生成滑動圖形的流程響應時間可以控制在 20ms 左右,如果原圖分辨率在 300px*150px 以下,可以到 10ms 左右,在可接受范圍內。
總結
以上是生活随笔為你收集整理的(附代码)手写滑动验证码,完整代码开放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android ANR 实例分析
- 下一篇: Android Camera调用流程