zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    单元测试的必要性
    【C++ STL】Queue
    【C++ STL】Stack
    【C++ STL】容器的选择
    【C++ STL】Map和Multimap
    [Effective JavaScript 笔记]第19条:熟练掌握高阶函数
    [Effective JavaScript 笔记]第18条:理解函数调用、方法调用及构造函数调用之间的不同
    node实现rar格式压缩
    [Effective JavaScript 笔记]第2章:变量作用域--个人总结
    [Effective JavaScript 笔记]第17条:间接调用eval函数优于直接调用
  • 原文地址:https://www.cnblogs.com/dankewoniu/p/8515762.html
Copyright © 2011-2022 走看看