ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JMeter请求加密参数实战:签名、加密与动态生成全攻略

JMeter请求加密参数实战:签名、加密与动态生成全攻略 Jmeter请求发送加密参数做接口测试最怕遇到什么不是接口逻辑复杂而是参数压根不让你明文看明白。头天还在测登录、下单第二天需求方甩过来一个报文示例里面带了个sign、encrypt、token说这是加密参数你们脚本自己想办法。这时候如果你还在用普通的 JMeter HTTP 请求直接拼参数那基本是跑不通的。这篇文章我就围绕 JMeter 请求发送加密参数这个场景把我在实际项目里踩过的坑、总结出来的套路一次性给你聊透。内容适合刚接触 JMeter 的新手也适合那些已经会跑脚本但一遇到 sign 就头疼的测试开发同学。很多人在遇到加密参数时第一反应是让开发先把加密关掉或者把返回值里的密文抄下来写死在脚本里。这种办法不是不行但只能撑过这一次测试。下次参数一变、时间戳一刷新、账号一切换写死的密文立刻失效用例就废了。真正靠谱的思路是我们自己把加密逻辑写进 JMeter让脚本在每次请求之前动态算出合法的加密参数这样无论参数怎么变脚本都能稳定跑。往下我会把怎么选工具、怎么写代码、怎么调试、怎么排查常见问题全部拆开讲清楚。1. 内容整体设计与思路拆解1.1 加密接口需求为什么越来越常见现在前后端联调接口传输的参数已经很少裸奔了。很多内部管理系统、开放平台、支付类接口都会对请求参数做两道处理一是签名保证参数没有被篡改二是加密防止敏感信息在传输过程中泄露。签名通常就是把所有请求参数按照一定规则拼接起来加上一个双方约定好的密钥用 MD5、SHA-256 或者 HMAC 系列算法计算出一个固定长度的字符串比如sign3f2b3d9a...。加密则是把某个参数值本身变成一段密文比如把手机号、身份证号用 AES 加密后再传出去服务端拿到密文再解密。站在测试角度这两类需求落到 JMeter 里面本质就一句话我们要在发送 HTTP 请求之前用同样的规则把签名或加密结果动态算出来然后塞到请求参数或者请求头里面。听起来不复杂但实际操作会碰到很多问题比如密钥放在哪里、用什么语言写加密逻辑、JMeter 的 BeanShell 和 JSR223 有什么区别、中文参数加密出来跟开发对不上怎么办。这篇文章就是把这些点一个接一个解决掉。1.2 落地 JMeter 之前的三个关键确认很多人拿到加密需求就直接打开 JMeter 开始写脚本结果折腾一个下午跑出来的结果永远和服务端对不上。根据我自己的经验动手之前必须先确认三件事。第一确认加密算法的完整定义。签名是把所有参数按 ASCII 排序还是按固定顺序拼接要不要拼接密钥密钥是加在参数中间还是最后面MD5 输出结果是大写还是小写加密算法是 AES 还是 DES填充模式是 PKCS5Padding 还是 PKCS7PaddingIV 向量是什么输出方式是 Base64 还是 Hex这些细节差一个字符结果就完全不一样。第二确认参数执行的顺序。有些接口要求先对参数值做 URLEncoder 编码然后才参与签名有些接口则要求先签名再把整个参数字符串做一次加密。顺序弄反了签名永远对不上。第三确认时间戳和随机数的精度。很多签名接口都会传timestamp和nonce随机字符串而且服务端会校验时间差超过 5 分钟直接拒绝。所以在脚本里生成的时间戳必须和当前请求时间保持同步不能写死一个常量。把这三件事拿小本本记清楚再去动手写 JMeter 脚本效率会高很多。我就见过一个同事密钥拼接规则少了一个冒号排查了整整一下午最后发现是文档更新了开发没同步给他。2. 核心细节解析与实操要点2.1 JMeter 里写加密逻辑的三种主流方式要在 JMeter 里动态计算加密参数常见方式有三种函数助手、前置处理器BeanShell PreProcessor / JSR223 PreProcessor、Java 请求采样器。函数助手适合非常简单的场景比如用${__time(yyyy-MM-dd HH:mm:ss)}生成时间戳用${__Random(100000,999999)}生成随机数。但它做不了多行逻辑更做不了复杂的加密运算所以只适合作为辅助手段。前置处理器是主力方案尤其是 JSR223 PreProcessor Groovy 脚本。很多老项目喜欢用 BeanShell PreProcessor但 JMeter 官方文档早就建议尽量用 JSR223因为 BeanShell 在性能上不如 Groovy而且在 JMeter 3.x 之后的版本里BeanShell 对 Java 新语法的支持也比较有限。JSR223 还有一个好处脚本里可以直接import项目里的 Java 类如果你的开发团队把加密逻辑封装成了工具类你甚至可以让开发把 jar 包给你直接在脚本里调用。Java 请求采样器是更彻底的做法直接用 Java 写一个采样器继承 JMeter 的抽象类把加密逻辑全部写在 Java 代码里。这种方式灵活度最高但开发成本也高一般用在加密逻辑极其复杂、脚本量非常大的场景。大多数项目用 JSR223 Groovy 就已经完全够用了。我个人给你的建议是优先用 JSR223 Groovy。下面所有实操案例我都是用这个组合写的你可以直接照抄。2.2 加密数据从哪来以及参数放哪里搞清楚了用什么工具还要搞清楚数据流。加密参数并不是平白无故生成的它需要依赖至少三类数据固定数据比如密钥、AppID、商户号。这类数据你可以在 JMeter 里定义一个用户自定义变量放到“用户定义的变量”组件里也可以在脚本中直接写一个常量字符串。写常量最简单但要注意别把密钥提交到代码仓库泄露了就很麻烦。请求数据比如下单金额、商品 ID、用户 ID。这类数据建议做成变量通过 CSV 数据文件或者 JSON 提取器从接口响应中取出来这样脚本才有通用性。动态数据比如时间戳、随机数。这类数据直接在脚本里生成每次请求都不一样。参数放在哪里也是一个容易搞混的点。加密参数可能在三个位置出现查询参数Query String、请求体Body、请求头Header。签名值sign一般放在查询参数或者请求头里加密后的密文一般放在请求体里。你在写 JSR223 脚本的时候最终一步就是把你算出来的值赋给 JMeter 变量然后在 HTTP 请求里面用${变量名}引用即可。3. 实操过程与核心环节实现3.1 第一个案例用 MD5 签名动态生成 sign这是最常见的情况。接口要求把除了sign之外的参数按照 key 的 ASCII 码升序排序拼接成key1value1key2value2的格式然后在最后拼上key你的密钥再做 MD5转成小写 hex。示例假设有三个参数namezhangsan、amount100、timestamp1728000000密钥是abc123那么参与签名的字符串就是amount100namezhangsantimestamp1728000000keyabc123对这个字符串做 MD5 后得到的值就是sign。在 JMeter 里这样做新建一个线程组添加一个 HTTP 请求默认值把协议、服务器地址、端口配好。在 HTTP 请求下面添加“前置处理器 - JSR223 PreProcessor”语言选择groovy。写脚本import java.security.MessageDigest; // 请求参数这里可以用 JMeter 变量传入 def name zhangsan; def amount 100; def timestamp System.currentTimeMillis() / 1000 as long; def secretKey abc123; // 1. 按 key 升序拼接参数这里我们手动排序 def params [name: name, amount: amount, timestamp: timestamp.toString()]; def keys params.keySet().toArray(); java.util.Arrays.sort(keys); def sb new StringBuilder(); for (def key : keys) { if (sb.length() 0) { sb.append(); } sb.append(key).append().append(params[key]); } sb.append(key).append(secretKey); def md5 MessageDigest.getInstance(MD5); byte[] digest md5.digest(sb.toString().getBytes(UTF-8)); // 转 hex def hexString new BigInteger(1, digest).toString(16); while (hexString.length() 32) { hexString 0 hexString; } // 时间戳和 sign 都存为变量 vars.put(timestamp, timestamp.toString()); vars.put(sign, hexString);然后在 HTTP 请求的路径或参数里直接引用${timestamp}和${sign}。注意一点JMeter 里vars.put()保存的都是字符串如果你需要数值类型在后面的断言里要自己转换。3.2 第二个案例AES 加密参数放进请求体有些接口反过来不是签名而是把某些字段加密后传输。比如下单接口要求mobile字段用 AES/ECB/PKCS5Padding 加密密钥是 16 位输出 Base64 字符串。在 JMeter 里用 JSR223 脚本处理这个就需要用到 Java 自带的加密库。脚本示例import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; def aesKey 1234567890abcdef; // 16位 def mobile 13800138000; def secretKeySpec new SecretKeySpec(aesKey.getBytes(UTF-8), AES); def cipher Cipher.getInstance(AES/ECB/PKCS5Padding); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] encrypted cipher.doFinal(mobile.getBytes(UTF-8)); def encryptedBase64 Base64.getEncoder().encodeToString(encrypted); vars.put(encryptedMobile, encryptedBase64);然后在 HTTP 请求的 Body Data 里面用 JSON 格式写入{ mobile: ${encryptedMobile} }有点要注意如果接口要求的输出不是 Base64 而是 Hex那就把最后一步改成把字节数组转成十六进制字符串方法和 3.1 里的 MD5 转 hex 一致。3.3 第三个案例HMAC-SHA256 签名并放入请求头现在越来越多的开放平台喜欢用 HMAC-SHA256因为它比单纯 MD5 更安全而且不需要把密钥拼到参数字符串里。常见的做法是sign Base64(HMAC-SHA256(secret, message))其中message可能是请求方法、请求路径、时间戳、随机数的拼接。JMeter 脚本示例import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; def appSecret your-secret-here; def method POST; def path /api/order/create; def timestamp System.currentTimeMillis() / 1000 as long; def nonce UUID.randomUUID().toString().replace(-, ); def message method \n path \n timestamp \n nonce; Mac mac Mac.getInstance(HmacSHA256); SecretKeySpec keySpec new SecretKeySpec(appSecret.getBytes(UTF-8), HmacSHA256); mac.init(keySpec); byte[] rawHmac mac.doFinal(message.getBytes(UTF-8)); def sign Base64.getEncoder().encodeToString(rawHmac); vars.put(timestamp, timestamp.toString()); vars.put(nonce, nonce); vars.put(sign, sign);然后把这三个变量放到 HTTP Header Manager 里面比如X-Timestamp、X-Nonce、X-Sign。这样每次请求都会自动带上一组合法的签名完全不需要人工干预。这类接口在实际项目中是最多的尤其是你在网上买云服务、调开放平台 API 的时候基本全是这种套路。3.4 国密 SM3 / SM4 怎么办还有一个绕不开的话题现在不少政务、金融、企业内部系统已经要求使用国密算法最常见的就是 SM3 摘要和 SM4 分组加密。JMeter 本身不带国密算法库但可以通过引入 Bouncy Castle 的 jar 包来支持。操作方法是把bcprov-jdk18on-xxx.jar放到 JMeter 的lib目录下重启 JMeter然后在 JSR223 脚本里调用import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.security.Security; Security.addProvider(new BouncyCastleProvider()); def sm4Key 0123456789abcdef; def data test; SecretKeySpec keySpec new SecretKeySpec(sm4Key.getBytes(UTF-8), SM4); Cipher cipher Cipher.getInstance(SM4/ECB/PKCS5Padding, BC); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] encrypted cipher.doFinal(data.getBytes(UTF-8)); def encryptedHex encrypted.collect { String.format(%02x, it) }.join(); vars.put(sm4Data, encryptedHex);SM3 摘要也类似调用MessageDigest.getInstance(SM3, BC)就能拿到摘要值。这里我特别想说一句别指望所有加密逻辑都在 JMeter 里从零开始写。如果开发团队已经写好了加解密的工具类最简单的方式是让开发把这部分代码打成 jar 包放进 JMeter 的lib/ext目录然后在 JSR223 里直接 import 调用这样脚本和开发逻辑保持同步开发改算法你也改脚本两边不容易出现偏差。4. 参数化扩展与多场景实测4.1 用 CSV 文件驱动多组加密参数你做接口测试不可能只测一组数据。拿下单来说你至少要测不同价格、不同商品、不同用户的场景。这时候就需要把请求参数从 CSV 文件里读入再用 JSR223 脚本读取变量计算签名。在 JMeter 里添加 CSV 数据文件设置设置变量名为name,amount,mobile然后在 JSR223 里用${name}或者vars.get(name)读取并参与加密。import java.security.MessageDigest; def name vars.get(name); def amount vars.get(amount); def timestamp System.currentTimeMillis() / 1000 as long; def secretKey abc123; def params [name: name, amount: amount, timestamp: timestamp.toString()]; def keys params.keySet().toArray(); java.util.Arrays.sort(keys); def sb new StringBuilder(); for (def key : keys) { if (sb.length() 0) { sb.append(); } sb.append(key).append().append(params[key]); } sb.append(key).append(secretKey); def md5 MessageDigest.getInstance(MD5); byte[] digest md5.digest(sb.toString().getBytes(UTF-8)); def hexString new BigInteger(1, digest).toString(16); while (hexString.length() 32) { hexString 0 hexString; } vars.put(timestamp, timestamp.toString()); vars.put(sign, hexString);这样 CSV 里每一行数据都会独立生成一个对应的签名脚本跑起来就像真实用户在多次下单数据之间互不干扰。这里我额外提醒一句如果 CSV 文件里的字段值含有中文JMeter 默认读取可能会乱码建议把 CSV 文件另存为 UTF-8 编码并在 CSV 数据文件设置里把文件编码改成UTF-8。4.2 动态 token 如何与服务端交互有些系统的加密签名不光依赖请求参数还依赖登录之后返回的token或者sessionId。也就是说你必须在登录接口里先拿到 token然后用这个 token 去参与后续接口的签名计算。在 JMeter 里实现这个思路很直接。首先在登录请求下面添加“后置处理器 - JSON Extractor”提取响应里的access_token字段保存到变量accessToken。然后在后续请求的 JSR223 脚本里读取这个变量拼进签名消息def accessToken vars.get(accessToken); def message accessToken timestamp nonce;这里面有一个非常容易踩的坑如果登录请求本身也带签名那就必须先实现登录接口的签名再去实现后续接口的签名并且两者之间要确保 token 已经成功提取。你可以在登录请求和后续请求之间加一个调试取样器先用 Debug Sampler 查看accessToken变量是否已经有值确认后再继续往下写。别一上来就把整套流程写完然后跑不通的时候根本不知道是签名问题还是提取问题。4.3 压测时加密参数对性能的影响很多人问我做压测的时候JSR223 脚本的加密计算会不会拉低吞吐量我的实测经验是用 JSR223 Groovy 做 MD5 或者 HMAC 签名单线程每秒可以执行几千到上万次就算 100 并发也不会有明显瓶颈。但如果用 BeanShell性能就会差很多尤其是脚本里频繁创建对象、写大量日志的时候CPU 占用率会明显上升压测结果也就不准了。所以在压测场景下加密逻辑尽量满足这几个要求使用 JSR223 Groovy 而不是 BeanShell。脚本里不写log.info()这类不必要的日志。不依赖外部文件 IO把密钥和参数都放在内存中。尽量复用对象比如把MessageDigest或者Cipher实例放在循环外当然 JSR223 脚本每次请求都会执行这里的循环外指的是脚本内层的 for 循环其实每一次请求脚本本身就会被重新创建所以真正影响性能的是脚本内部的算法复杂度不是对象创建的开销。我在实际压测中遇到过一个问题脚本写的比较复杂里面有大量字符串拼接和 Base64 编码解码结果 500 并发下响应时间明显飙升。后来把不必要的字符串操作简化掉吞吐量立刻上来了。所以压测之前先用少量线程跑一遍观察聚合报告中的响应时间分布判断加密逻辑是否是瓶颈再决定要不要优化。5. 常见问题与排查技巧实录5.1 签名结果和服务端不一致这是出现频率最高的问题。我总结了一个排查顺序按这个顺序来基本能在五分钟内定位问题先检查字符集。开发那边如果用的是 UTF-8你脚本里用了默认编码JMeter 3.x 之前默认可能是 ISO-8859-1签名字节流就不一样结果肯定不一致。统一改成 UTF-8重跑。再检查大小写。hexString.toString(16)得到的是小写开发可能要求大写那就调用toUpperCase()。反过来如果开发返回的是小写而你转成了大写同样对不上。然后检查排序方式。你按 ASCII 排序开发可能用的是 TreeMap 按 key 自然排序对于纯字母 key 两者基本等价但如果 key 里面有下划线或者数字排序结果可能不同要仔细核对签名规则。最后检查空值处理。有些参数值为空时开发会直接忽略该参数但你的脚本可能把空值也拼进签名串了。这属于规则细节只能看接口文档。5.2 中文参数加密后乱码中文参数在加密前必须统一编码。签名规则里通常会写明“先把参数值做 URLEncode然后再参与签名”如果你漏掉这一步开发那边先编码后签名你直接拿原始中文签名结果必然不同。在 Groovy 里可以用URLEncoder.encode(value, UTF-8)处理。加密场景更要注意把中文转成字节数组时明确指定getBytes(UTF-8)不要用默认平台编码否则在 Windows 上开发和 Linux 服务器上跑出来的结果会不一样。另外还有一个场景加密参数最终是要放到 HTTP 请求里的。如果在路径中传中文加密值JMeter 的HTTP Request在默认情况下可能不做二次编码服务端收到后解析异常。这种时候可以给请求添加“HTTP Header Manager”加上Content-Type: application/json; charsetutf-8并把请求参数写法也统一成 UTF-8。5.3 请求体格式错误导致服务端报 400很多加密参数是以 JSON 格式发送的但 JMeter 里如果你在 Body Data 中写 JSON同时又加了一堆 Query 参数就可能出现格式验证失败。另外 JSON 里如果包含${sign}这样的变量引用变量未被替换时JMeter 会原样发送服务端解析失败。我常用的排查方法加一个“查看结果树”打开请求体直接看服务端实际收到的内容是什么。如果发现${sign}没有被替换说明 JSR223 脚本没执行成功需要看脚本里的报错信息。如果发现 sign 值是一个空字符串说明vars.put()没有把值写对可能是变量名拼写错误。5.4 BeanShell 性能问题与多线程并发冲突如果你还在用 BeanShell 写加密逻辑高并发下容易踩一个坑BeanShell 解释器不是线程安全的多个线程同时执行同一个 BeanShell PreProcessor 时可能出现变量相互覆盖或者脚本执行异常。这种情况在普通功能测试里不容易发现但一旦压测跑到 100 并发以上问题就会冒出来。解决方案很简单切换到 JSR223 Groovy。Groovy 引擎本身对并发支持更好而且 JMeter 官方对 JSR223 的推荐程度也远高于 BeanShell。如果你有历史脚本是用 BeanShell 写的迁移成本也不高大部分代码逻辑可以直接搬过去只是个别语法需要微调。5.5 常见问题速查表现象可能原因解决办法签名不一致字符集不一致 / 大小写不一致 / 密钥拼错统一 UTF-8确认输出大小写检查密钥拼接规则签名不一致参数排序方式不同和开发核对排序规则用 TreeMap 或者手动排序保持严格一致中文乱码编码没有明确指定所有getBytes()都写成getBytes(UTF-8)返回 400请求体 JSON 格式错误查看结果树检查实际发送内容修正 Body Data变量没有被替换JSR223 脚本执行出错打开 JSR223 的日志输出点击运行查看报错日志密文很长且带 / Base64 结果在 URL 被转义对密文做 URLEncoder 编码或放到请求体中传递压测吞吐量低BeanShell 性能问题换成 JSR223 Groovy减少脚本内部日志输出高并发下变量错乱BeanShell 线程不安全切换 Groovy避免共享变量提示排查加密问题时最直接的办法是把开发那边的加密结果拿来对比把你们的输入字符串和输出结果打印出来一行一行对。实际工作中很多问题就是多了一个空格、少了一个 符号导致的。5.6 调试加密脚本的三个实用技巧第一个技巧是使用log.error()输出中间值。在 JSR223 脚本里写log.error(params)或者log.error(sign message: sb.toString())运行后去 JMeter 日志里查看不用看结果树翻找效率更高。用完记得注释掉不然压测时日志刷屏。第二个技巧是加一个 Debug Sampler。在线程组下添加“Debug Sampler”它会把当前所有 JMeter 变量输出到查看结果树中一次就能看到所有加密相关的变量值方便快速确认变量是否赋值成功。第三个技巧是写一个独立的小方法验证算法。如果你不确定某个加密算法在 Groovy 中的写法是否正确可以在本地用 Java 或者 Python 先实现一遍用同样的输入跑一遍和开发给的值对一下。确认算法没问题再往 JMeter 脚本里搬。这一步看着多余但能省下大把排查时间。5.7 证书和 HTTPS 场景的额外补充加密参数要动态生成如果接口本身还是 HTTPS 的JMeter 还需要处理证书问题。有些项目用的是自签名证书或者内网测试证书JMeter 默认的 HttpClient 实现可能会报证书验证失败。解决方案有两种一种是在 JMeter 的系统属性里配置信任所有证书另一种是让开发导出测试证书用 keytool 导入 JMeter 使用的 JRE 证书库。实际项目中我一般先把自签名证书导入到 JMeter 运行的 JDK 里然后配置jmeter.properties里的server.rmi.ssl.disabletrue这是用于分布式压测的场景单机测试不用管这个。更简单的做法是如果你确认测试环境安全可以在 SSL 管理器里导入证书或者直接用 HTTP 跳过证书校验相关配置。不过这类问题跟加密参数本身关系不大不是这篇文章的重点。6. 完整项目示例串联6.1 一个真实场景登录后带签名创建订单现在我把前面所有知识串起来模拟一个完整场景系统要求先登录获取 token然后用 token、时间戳、随机数对订单参数做 HMAC-SHA256 签名最后把加密后的手机号一起以 JSON 格式发送。测试计划结构如下测试计划 ├── 用户定义的变量 │ ├── appId test-app │ ├── appSecret abc123def456ghi789 │ └── serverHost api.example.com ├── 线程组 │ ├── 登录请求 │ │ ├── HTTP Header Manager │ │ ├── JSR223 PreProcessor (生成登录签名) │ │ └── JSON Extractor (提取 accessToken) │ ├── 创建订单请求 │ │ ├── HTTP Header Manager │ │ ├── JSR223 PreProcessor (生成订单签名 加密手机号) │ │ └── 查看结果树 │ └── 聚合报告登录请求的 JSR223 脚本用 HMAC-SHA256 生成sign、timestamp、nonce放到请求头。拿到响应后JSON Extractor 提取access_token。创建订单请求的 JSR223 脚本读取accessToken用它参与签名同时对mobile字段做 AES 加密最终把加密结果赋值给变量Body Data 里引用。大致脚本逻辑如下import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; def appSecret vars.get(appSecret); def accessToken vars.get(accessToken); def timestamp System.currentTimeMillis() / 1000 as long; def nonce UUID.randomUUID().toString().replace(-, ); def bodyStr amount100mobile13800138000; def message accessToken \n timestamp \n nonce \n bodyStr; Mac mac Mac.getInstance(HmacSHA256); SecretKeySpec keySpec new SecretKeySpec(appSecret.getBytes(UTF-8), HmacSHA256); mac.init(keySpec); def sign Base64.getEncoder().encodeToString(mac.doFinal(message.getBytes(UTF-8))); vars.put(timestamp, timestamp.toString()); vars.put(nonce, nonce); vars.put(sign, sign);这个示例就是把前面讲的签名、加密、变量传递、后置提取全部串起来。你在自己的项目里复制这个框架替换成实际接口的参数名和算法规则就能跑通大多数加密接口测试。6.2 从功能测试到压测的平滑过渡这个脚本验证通过之后把它作为压测脚本使用也很简单。注意几个点登录请求一般只需执行一次可以把“登录请求”放在单独的线程组里或者通过Once Only Controller控制。后续压测时给“创建订单请求”单独设置线程数和循环次数这样就不会每次迭代都走一遍登录流程压测结果更接近真实情况。如果你需要模拟多账号并发基于用户维度压测可以把 token 列表提前准备成一个 CSV 文件每个线程读取一行登录接口需要在 JSR223 脚本中动态拿到用户信息。这种做法比较适合并发量不是特别大的场景如果并发过千建议先做一次大规模登录把 token 批量保存成文件再从文件读取避免登录接口成为压测瓶颈。6.3 加密参数的代码管理建议脚本里的密钥、AppSecret 属于敏感信息建议单独放到一个配置文件中通过 JMeter 的-J参数传进去或者放到用户自定义变量中但不要在博客、公共代码库中明文暴露。如果公司对密钥管理有要求可以考虑让开发提供一个加解密接口测试环境单独配置一套测试密钥。总之测试脚本的安全性也需要重视不能让项目密钥随便泄露出去。7. 避坑心得与个性化建议7.1 加密算法更新时的应对方式测试过程中最怕开发突然改签名规则。比如把 MD5 换成了 SHA-256或者把拼接顺序改了一下你的脚本立刻全挂。面对这种情况我的建议是在脚本里把算法名、密钥、拼接规则单独抽取成变量不要写死在代码中。这样改起来只需改配置不用动核心脚本。甚至可以更简单一点JSR223 脚本里读取 JMeter 属性或者用户参数让开发和测试共用一套配置算法调整只改配置两边同时生效。7.2 要不要专门学习 Groovy 语言很多人看到要在 JMeter 里写 Groovy 脚本就犯怵觉得自己不会这门语言。其实你大可以放心Groovy 的语法和 Java 非常相近你只需要学会定义变量、调用方法、拼接字符串这些基础内容就能应付绝大多数加密逻辑。不需要系统的学完整本 Groovy 教程而是遇到什么加密算法搜索对应的 Groovy 写法改一改就能用。用了两三周之后你自然就熟练了。7.3 什么时候应该考虑放弃 JMeter加密参数如果极其复杂比如每次请求都要做 RSA 非对称加密、动态获取密钥、二次加签而且密钥轮换频繁JMeter 的脚本维护成本就会变得很高。这种场景下我反而建议你考虑用 Python 的requests库搭配 PyCryptodome 写脚本或者用 Postman 的 Pre-request Script 做接口调试再用 Locust 做压测。工具选型不是死的JMeter 在性能测试领域依然是标杆但在某些过于复杂的接口交互场景下别的工具可能更顺手。我在实际工作就是这样切换的需求简单、数据量大、压测任务重的项目用 JMeter需要快速实现复杂加密流程、做详细逻辑校验的项目用 Python。两边互补效果不错。8. 写在最后的个人体会做接口测试这两年我最大的体会是加密参数并不难难的是你愿不愿意静下心去理解算法规则。很多人一看到sign、encrypt就说要开发帮忙或者干脆绕过测试这其实都是在给自己省事的同时埋了坑。真正把 JMeter 脚本跑起来自己动手把 MD5、HMAC、AES 一个个复现出来之后你会发现这些算法其实都有固定的套路一旦跑通第一个后面全部可以复制。最后再分享一个小技巧在 JSR223 脚本里写加密逻辑时尽量在脚本最顶部加一行import把需要的类全部引进来。虽然 Groovy 默认支持一部分 Java 类直接调用但显式引入可以提高可读性也方便你后续把脚本交给别人维护。每次运行脚本前先取消注释log.error()看一遍参数拼接过程确认无误后再注释掉。这种习惯看着繁琐但真的能帮你避免很多低级错误。
返回列表