Jmeter MD5插件


實際業務中,會要求 HTTP 協議中附加 MD5 校驗字段, 防止請求參數被惡意篡改, 對於開發同學來說, 這是個很簡單的需求。 但是給自動化測試增加了難度, Jmeter 原生不支持這個功能,應測試同學要求,開發了一個簡單的小插件。

一、功能分析

  1. MD5 值生成規則:

    Step1, HTTP 請求增加新參數 ct, 值為當前時間毫秒數;

    Step2, 獲取HTTP 請求所有參數,放入 list;

    Step3, List 增加新參數 key, 值根據通信協議的雙方約定(不在 http請求中傳遞 );

    Step4, 把 List 按參數名進行排序,把排序后的參數值進行拼接, 對拼接結果執行 MD5 運算;

    Step5, Http請求增加新參數 code, 值是 Step4 計算得出的值。

  注:不同業務此規則可能略有差異,請根據實際情況實現。

  2. MD5 值需要在 HTTP 請求之前添加 (屬於 Jmeter 前置處理器范圍)。

 

二、需要解決的問題

  1. 在 Jmeter 的 HTTP 請求中獲取參數、 增加參數

  2. 實現 MD5 的具體邏輯

  3. 在合適的時機把生成的 MD5 校驗值附加到 HTTP 請求參數中

  4. 集成到 Jmeter 菜單中,方便使用。

 

三、解決步驟

  1. 因之前沒有 Jmeter 插件開發經驗, 需要先學習一下如何開發。 網上搜集了一下資料沒有太明確的方案;不過Jmeter本身有很多插件,就從這里入手,分析一下現有插件的實現方式,然后用相同的方式來完成插件。

  2. 到 Apache 官網 下載 Jmeter 的源代碼(我使用的2.11版), 根據 Jmeter 界面中看到的關鍵字來定位插件代碼位置。

  3. 開發的是 Http Sampler 插件,那么,我們先搜索一下Http Sampler關鍵字, 可以找到 HttpSampler.java;閱讀代碼可以得到問題1的答案。 

  4. 問題2不在贅述。

  5. 分析問題3, 我們需要在 HTTP 請求發送前, 進行插件調用, 可以得知這個插件應該是屬於前置處理器范圍的。 通過分析前置處理器的代碼應該可以解決這個問題。Jmeter 插件中第一個前置處理器是 BeanShell PreProcessor, 用 BeanShell 和 PreProcessor 關鍵字查找源代碼, 查到了類 BeanShellPreProcessor.java;閱讀這個類及父類代碼可以得到問題3、問題4的答案。

 

四、具體代碼

1. MD5PreProcessor.java

  1 public class MD5PreProcessor  extends AbstractTestElement implements TestStateListener, PreProcessor, Serializable {
  2 
  3     private static final long serialVersionUID = -1L;
  4     private static final Logger LOGGER = LoggingManager.getLoggerForClass();
  5 
  6     /**
  7      * Default constructor.
  8      */
  9     public MD5PreProcessor() {
 10     }
 11 
 12     @Override
 13     public void testStarted() {
 14     }
 15 
 16     @Override
 17     public void testStarted(String host) {
 18     }
 19 
 20     @Override
 21     public void testEnded() {
 22     }
 23 
 24     @Override
 25     public void testEnded(String host) {
 26     }
 27 
 28     /*
 29      * ------------------------------------------------------------------------
 30      * Methods implemented from interface org.apache.jmeter.config.Modifier
 31      * ------------------------------------------------------------------------
 32      */
 33 
 34     @Override
 35     public void process() {
 36         Sampler sam = getThreadContext().getCurrentSampler();
 37         HTTPSamplerBase sampler = null;
 38         if (!(sam instanceof HTTPSamplerBase)) {
 39             return;
 40         } else {
 41             sampler = (HTTPSamplerBase) sam;
 42         }
 43 
 44         // 排序參數和header(appversion, os)
 45         java.util.List<KeyValue> list = new ArrayList<KeyValue>();
 46 
 47 
 48         sampler.addArgument("ct", System.currentTimeMillis() + "");
 49 
 50         // 添加參數列表
 51         Arguments arguments = sampler.getArguments();
 52         PropertyIterator iter = arguments.iterator();
 53         while(iter.hasNext()) {
 54             Argument arg = getFromJMeterProperty(iter.next());
 55             KeyValue kv = new KeyValue(arg.getName(), arg.getValue());
 56             list.add(kv);
 57         }
 58 
 59         // 添加header: os appVersion
 60         HeaderManager headerManager = sampler.getHeaderManager();
 61         for(int i = 0; i < headerManager.getHeaders().size(); i++) {
 62             Header a = headerManager.getHeader(i);
 63             if("os".equals(a.getName()) || "appVersion".equals(a.getName())) {
 64                 list.add(new KeyValue(a.getName(), a.getValue()));
 65             }
 66         }
 67 
 68 
 69         // 加鹽
 70         list.add(new KeyValue("key", "+MrK}6seb#$E"));
 71 
 72         StringBuffer sbu = new StringBuffer();
 73 
 74         // 按順序拼接所有參數值
 75         Collections.sort(list);
 76         for(KeyValue kv : list) {
 77             //LOGGER.info("key: "+kv.getKey()+", value:"+kv.getValue());
 78             sbu.append(kv.getValue());
 79         }
 80 
 81         try {
 82             MessageDigest md = MessageDigest.getInstance("MD5");
 83             byte[] md5sum = md.digest(URLEncoder.encode(sbu.toString(), "UTF-8").getBytes());
 84             String md5Code = new BigInteger(1, md5sum).toString(16);
 85 
 86             while(md5Code.length() < 32 ){
 87                 md5Code = "0"+md5Code;
 88             }
 89 
 90             LOGGER.info("before: "+sbu.toString()+",encode:"+URLEncoder.encode(sbu.toString(), "UTF-8")+",end:"+md5Code);
 91             sampler.addArgument("code", md5Code);
 92         } catch(Exception e) {
 93             e.printStackTrace();
 94         }
 95 
 96 
 97 
 98     }
 99 
100     private Argument getFromJMeterProperty(JMeterProperty o) {
101         return (Argument)o.getObjectValue();
102     }
103 
104     /*
105      * ------------------------------------------------------------------------
106      * Methods
107      * ------------------------------------------------------------------------
108      */
109     private class KeyValue implements Comparable<KeyValue> {
110         private String key;
111         private String value;
112 
113         public KeyValue(String key, String value) {
114             this.key = key;
115             this.value = value;
116         }
117 
118         @Override
119         public int compareTo(KeyValue o) {
120             int  result = this.key.compareTo(o.key);
121             if(result == 0) {
122                 result = this.value.compareTo(o.value);
123             }
124 
125             return result;
126         }
127 
128         public String getKey() {
129             return key;
130         }
131 
132         public String getValue() {
133             return value;
134         }
135     }
136 
137 }
View Code

 

2. Md5PreProcessorGui.java

public class Md5PreProcessorGui extends AbstractPreProcessorGui {
    private static final long serialVersionUID = 100L;
    private static final Logger LOGGER = LoggingManager.getLoggerForClass();

    public Md5PreProcessorGui() {
        createGui();
    }

    public String getStaticLabel() {
        return "MD5 Encode";
    }

    public String getLabelResource() {
        return getClass().getCanonicalName();
    }

    public void configure(TestElement element) {
        super.configure(element);
    }

    public TestElement createTestElement() {
        MD5PreProcessor sampler = new MD5PreProcessor();
        modifyTestElement(sampler);
        return sampler;
    }

    public void modifyTestElement(TestElement element) {
        element.clear();
        configureTestElement(element);
    }

    public void clearGui() {
        super.clearGui();

    }

    private void createGui() {
        setLayout(new BorderLayout(0, 5));
        setBorder(makeBorder());

        this.setLayout(new BorderLayout(0, 5));
        this.setBorder(this.makeBorder());
        this.add(this.makeTitlePanel(), "North");

        VerticalPanel mainPanel = new VerticalPanel();
        add(mainPanel, "Center");

    }
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM