Jmeter—實現識別驗證碼登陸

在作自動化測試或壓力測試時,驗證碼老是一個問題。在以往的壓力測試經歷中,測試通常在獨立的測試環境中進行,能夠放心禁用驗證碼或使用萬能驗證碼,這個是最實用的。可是,這兩天我嘗試了一個使用第三方的圖形圖像識別工具來完成驗證碼識別並經過Jmeter完成登陸的過程,識別工具的識別成功率有限,所以本篇估計僅能在理論範圍內適用。html

本篇內容大部份內容來自於該做者的文章:http://blog.csdn.net/xreztento/article/details/48682923java

整體目的:給Jmeter寫一個後置處理器,用來將上一個請求響應返回的驗證碼圖片識別成文字,並將識別內容保存爲Jmeter的一個參數,這個參數供登陸post請求進行登陸驗證,從而完成登陸的自動化過程。ios

 

工具apache

 (1)第三方圖形圖像識別工具:tesseract-ocr  下載地址:http://code.google.com/p/tesseract-ocr/downloads/list  基本沒法下載,已上傳到個人百度網盤app

 安裝後,能夠在cmd下試一試是否安裝成功:編輯器

 在cmd下輸入命令:tesseract d:\123.jpg result -l eng   意思是將D盤下的123.jpg 識別後放在result.txt下ide

 

 (2)須要用到的jar包:工具

 Jmeter插件開發相關的jar包: ApacheJmeter_core.jar jorphon.jar logkit-2.0.jar  這些在Jmeter的lib中都有 直接導入工程項目便可post

 圖形處理相關的jar包:jai-imageio-1.1.jar  swingx-1.6.1.jar  從網上下的,已上傳到百度雲盤 jar 文件夾下學習

插件開發

用java IDE新建一個工程項目,實現兩個部分,一個是識別圖片,一個是Jmeter插件的UI部分。工程項目完成目錄爲:

package com.test.huu;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;

import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam;

public class ImageIOHelper{
    public static File createImage(File imageFile, String imageFormat) {

        File tempFile = null;
        ImageInputStream iis = null;
        ImageOutputStream ios = null;
        ImageReader reader = null;
        ImageWriter writer = null;

        try {
            Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(imageFormat);
            reader = readers.next();

            iis = ImageIO.createImageInputStream(imageFile);
            reader.setInput(iis);

            IIOMetadata streamMetadata = reader.getStreamMetadata();
                        TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.CHINESE);
            tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("tiff");
            writer = writers.next();

            BufferedImage bi = removeBackgroud(reader.read(0));
            IIOImage image = new IIOImage(bi,null,reader.getImageMetadata(0));
            tempFile = tempImageFile(imageFile);

            ios = ImageIO.createImageOutputStream(tempFile);
            writer.setOutput(ios);
            writer.write(streamMetadata, image, tiffWriteParam);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(iis != null){
                try {
                    iis.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(ios != null){
                try {
                    ios.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(writer != null){
                writer.dispose();
            }
            if(reader != null){
                reader.dispose();
            }

        }
        return tempFile;
    }

    private static File tempImageFile(File imageFile) {
        String path = imageFile.getPath();
        StringBuffer strB = new StringBuffer(path);
        return new File(strB.toString().replaceFirst("jpg", "tif"));
    }
    
    //給圖片降噪 提升識別度
    public static int isFilter(int colorInt) {  
        Color color = new Color(colorInt);
        if ((color.getRed() > 85 && color.getRed() < 255) 
                && (color.getGreen() > 85 && color.getGreen() < 255) 
                && (color.getBlue() > 85 && color.getBlue() < 255)) {  
            return 1;  
        }  
        return 0;
    }
    
    public static BufferedImage removeBackgroud(BufferedImage img)  
            throws Exception {  
        int width = img.getWidth();  
        int height = img.getHeight();  
        for (int x = 0; x < width; ++x) {  
            for (int y = 0; y < height; ++y) {  
                if (isFilter(img.getRGB(x, y)) == 1) {  
                   img.setRGB(x, y, Color.WHITE.getRGB());  
                }
            }  
        }  
        return img;
    } 
    
}
ImageIOHelper.java
package com.test.huu;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class OCR {
    private final String LANG_OPTION = "-l";
    private final String EOL = System.getProperty("line.separator");
    private String tessPath = "D://Program Files (x86)//Tesseract-OCR";

    public String recognizeText(File imageFile,String imageFormat) {
        File tempImage = ImageIOHelper.createImage(imageFile,imageFormat);
        File outputFile = new File(imageFile.getParentFile(),"output" + imageFile.getName());
        StringBuffer sb = new StringBuffer();
        List<String> cmd = new ArrayList<String>();

        cmd.add(tessPath+"//tesseract");
        cmd.add("");
        cmd.add(outputFile.getName());
        cmd.add(LANG_OPTION);
        cmd.add("eng");     
        ProcessBuilder pb = new ProcessBuilder();
        pb.directory(imageFile.getParentFile());

        cmd.set(1, tempImage.getName());
        pb.command(cmd);
        pb.redirectErrorStream(true);

        Process process = null;
        BufferedReader in = null;
        int wait;
        try {
            process = pb.start();
            //tesseract.exe xxx.tif 1 -l eng
            wait = process.waitFor();
            if(wait == 0){
                in = new BufferedReader(new InputStreamReader(new FileInputStream(outputFile.getAbsolutePath()+".txt"),"UTF-8"));
                String str;
                while((str = in.readLine())!=null){
                    sb.append(str).append(EOL);
                }
                in.close();

            }else{

                tempImage.delete();
            }
            new File(outputFile.getAbsolutePath()+".txt").delete();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        tempImage.delete();
        return sb.toString();
    }
}
OCR.java
package com.test.huu;

import java.io.File;  
  
public class TestOCR {  
  
 public static void main(String[] args) {  
        String path = "D://124.jpg";       
        System.out.println("ORC Test Begin......");  
        try {       
            String valCode = new OCR().recognizeText(new File(path), "jpeg");       
            System.out.println(valCode);       
        } catch (Exception e) {    
            e.printStackTrace();    
        }         
        System.out.println("ORC Test End......");  
    }    
  
}  
TestOCR.java
package com.test.huu;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;

import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.AbstractScopedTestElement;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

public class VcodeExtractor extends AbstractScopedTestElement implements PostProcessor, Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    /**
     * 
     */
    private static final Logger log = LoggingManager.getLoggerForClass();
    
    @Override
    public void process() {

        // TODO Auto-generated method stub
        JMeterContext context = getThreadContext();
        SampleResult previousResult = context.getPreviousResult();
        if (previousResult == null) {
            return;
        }
        log.debug("VcodeExtractor processing result");

        String status = previousResult.getResponseCode();
        int id = context.getThreadNum();
//        String imageName = id + ".jpg";
        String path = "D://" + id + ".jpg";

        if(status.equals("200")){
            byte[] buffer = previousResult.getResponseData();
            FileOutputStream out = null;
            File file = null;
            try {
                file = new File(path);
                out = new FileOutputStream(file);
                out.write(buffer);
                out.flush();

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if(out != null){
                    try {
                        out.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

              try {   
                String vcode = new OCR().recognizeText(file, "jpeg"); 
                vcode = vcode.replace(" ", "").trim();
                
                JMeterVariables var = context.getVariables();
                var.put("vcode", vcode);
//                var.put("vuser", String.valueOf(id));
                } catch (Exception e) {
                    e.printStackTrace();
                }    
        }

    }

}
VodeExtractor.java
package com.test.huu;
import org.apache.jmeter.processor.gui.AbstractPostProcessorGui;
import org.apache.jmeter.testelement.TestElement;

public class VcodeExtractorGUI extends AbstractPostProcessorGui{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**
     * 
     */
    @Override
    public TestElement createTestElement() {
        // TODO Auto-generated method stub
        VcodeExtractor extractor = new VcodeExtractor();
        modifyTestElement(extractor);
        return extractor;
    }

    @Override
    public String getLabelResource() {
        // TODO Auto-generated method stub
        return this.getClass().getName();
    }

    @Override
    public String getStaticLabel() {//設置顯示名稱
        // TODO Auto-generated method stub
        return "VcodeExtractor";
    }

    @Override
    public void modifyTestElement(TestElement extractor) {
        // TODO Auto-generated method stub
        super.configureTestElement(extractor);

    }
}
VcodeExtractorGUI.java

 

插件生成

插件開發完成後,在Eclipse中 export-Runnable jar file ,將必要的依賴庫加進去,最後會生成一個 .jar 文件

注意:圖形相關的jar包  直接使用時會報錯(Jmeter會報一個錯:java.lang.IllegalArgumentException: vendorName == null) 最終在網上找到了解決方案

生成jar包後,用解壓工具打開,將 /META-INF 目錄下的 MANIFEST.MF 文件用編輯器(我用的是sublime)打開,拷貝進去下面一段代碼,保存壓縮包:

Specification-Title: Java Advanced Imaging Image I/O Tools
Specification-Version: 1.1
Specification-Vendor: Sun Microsystems, Inc.
Implementation-Title: com.sun.media.imageio
Implementation-Version: 1.1
Implementation-Vendor: Sun Microsystems, Inc.

 

插件插入Jmeter

將 .jar 文件放入Jmeter 安裝路徑下 lib/ext/ 目錄下,重啓Jmeter

能夠看到,咱們新開發的後置處理器 VcodeExtractor 

再看下大體的登陸過程測試計劃:

 

登陸的post請求參數中,可使用Vcode,Vcode是咱們開發的後置處理器 VcodeExtrator 返回的從圖片驗證碼中識別出來的字符串

 

插件效果驗證

把測試計劃跑一次  根據察看結果樹  看下效果

 

登陸請求成功啦,可是圖片識別也不是百分百成功,部分失敗狀況下,登陸請求確定會失敗。Tesseract-OCR也有訓練識別的功能,可是再也不繼續研究了。

 

關於Tesseract-OCR的延展性學習可參考:

(1)http://www.cnblogs.com/alex-blog/archive/2012/10/08/2714984.html

(2)http://blog.csdn.net/ycb1689/article/details/8520954

(3)http://www.52itstyle.com/thread-4803-1-1.html

相關文章
相關標籤/搜索