一、先从网上下载一个OCRserver

网上很多,找到一个能用的就行

后面Jmeter调用的时候要保证OCRserver处于运行状态

OCRserver启动后,ip和端口是固定的

二、Jmeter获取验证码,并通过OCRserver识别

可以用如下结构形式

1、获取验证码

这个按照实际调用的验证码接口即可

2、保存相应结果到文件

另:还有一种情况,虽然是图片验证码,但因为前端框架原因,返回结果不是图片,而是二进制信息,这时候就需要,把二进制图片保存为图片

使用JSR223后置处理器

import java.io.FileOutputStream
import java.text.SimpleDateFormat

// 获取接口的二进制响应数据(图片原始数据)
byte[] imageData = prev.getResponseData()

// 生成唯一的文件名(时间戳+随机数避免重复)
def timestamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())
def random = (int)(Math.random() * 1000)
def fileName = "captcha_${timestamp}_${random}.png"

// 定义保存路径(可根据需要修改,例如"./captcha/"表示当前目录下的captcha文件夹)
def savePath = "写一个保存路径"

// 创建完整的文件对象
def file = new File(savePath + fileName)

// 确保目录存在,如果不存在则创建
file.getParentFile().mkdirs()

try {
    // 写入图片数据到文件
    FileOutputStream fos = new FileOutputStream(file)
    fos.write(imageData)
    fos.close()
    
    // 获取文件全路径
    def fullPath = file.getAbsolutePath()
    
    // 将全路径存入cap变量,供后续步骤使用
    vars.put("cap", fullPath)
    
    log.info("验证码图片保存成功:${fullPath}")
    log.info("已将路径存入cap变量:${vars.get('cap')}")
} catch (Exception e) {
    log.error("验证码图片保存失败:${e.getMessage()}")
    // 出错时可以设置一个空值或标记
    vars.put("cap", "")
}

3、将保存的图片转换成base64,使用JSR223Sampler

语言选择groovy

import java.io.*;
import org.apache.commons.codec.binary.Base64;

// 取保存响应到文件中的验证码变量结果
String imagePath = vars.get("cap");

// 检查变量是否存在且不为空
if (imagePath == null || imagePath.trim().isEmpty()) {
    log.error("变量 'cap' 未设置或为空值");
    // 可以设置一个默认路径或者直接返回
    return;
}

byte[] data = null;

try {
    // 检查文件是否存在
    File imageFile = new File(imagePath);
    if (!imageFile.exists()) {
        log.error("文件不存在: " + imagePath);
        return;
    }
    
    InputStream in = new FileInputStream(imageFile);
    data = new byte[in.available()];
    in.read(data);
    in.close();

    Base64 base64 = new Base64();
    vars.put("base64", base64.encodeToString(data));
    log.info("成功将文件转换为Base64编码: " + imagePath);

} catch (IOException e) {
    log.error("处理文件时发生错误: " + e.getMessage());
    e.printStackTrace();
}

4、创建新的http请求,用来调用OCRserver服务

并且使用后置处理器的json提取器获取到识别出的验证码

拿到验证码之后就可以进行使用了

三、使用样例

当前测试的场景是,某接口,需要主管复核之后,才能提交请求。图形验证码是用在复核接口。

但ocr识别可能不准确,这样复核接口失败,就不需要再调用下一个接口

因此使用的结构是这样的

使用while循环,只有符合通过才跳出循环

${__jexl3(vars.get("should_retry") == "true",)}

先定义重试标志

在while循环前增加重置标志的逻辑

String should_retry = vars.get("should_retry");
String terminate = vars.get("terminate");

//log.info('----------------------------------------------')
//log.info('重置前should_retry:'+should_retry);
//log.info('重置前terminate:'+terminate);

// 重置状态变量
vars.put("should_retry", "true")
vars.put("terminate", "false")

// 打印时直接从vars获取最新值,而不是用之前的局部变量
//log.info('重置后should_retry:'+vars.get("should_retry"));
//log.info('重置后terminate:'+vars.get("terminate"));
//log.info('----------------------------------------------')

由于接口会返回以下几个场景

  • 复核成功
  • 验证码错误
  • 其他错误

所以需要区分判断

// 获取提取的返回码(此处code/desc是局部变量,后续未修改,无需调整)
String code = vars.get("responseCode");
String desc = vars.get("responseDesc");
// 注意:此处should_retry/terminate是局部变量,仅用于存储初始值(若后续不打印初始值,甚至可省略这两行)
//String should_retry_init = vars.get("should_retry");
//String terminate_init = vars.get("terminate");

//log.info('----------------------------------------------')
//log.info('复核接口调用前should_retry:'+should_retry_init); // 打印初始值(可选)
//log.info('复核接口调用前terminate:'+terminate_init);     // 打印初始值(可选)
//log.info('复核接口调用后responseCode:'+code);
//log.info('复核接口调用后responseDesc:'+desc);

if ("0000".equals(code)) {
    // 成功 - 继续后续接口
    log.info('复核成功 - 继续后续接口')
    vars.put("should_retry", "false")
    vars.put("terminate", "false")
} else if ("9999".equals(code) && desc.contains("验证码失效或错误请点击验证码重新获取")) {
    // 特定失败 - 需要重试
    log.info('特定失败 - 需要重试')
    vars.put("should_retry", "true")
    vars.put("terminate", "false")
} else {
    // 其他错误 - 终止测试
    log.info('其他错误 - 终止测试')
    vars.put("should_retry", "false")
    vars.put("terminate", "true")
}

// 关键修复:从vars中获取修改后的最新值,而非使用局部变量
//log.info('复核接口调用后should_retry:'+vars.get("should_retry"));
//log.info('复核接口调用后terminate:'+vars.get("terminate"));
//log.info('----------------------------------------------')

最后需要最终请求的接口外层套上if控制器,这样就可以只有复核通过时再调用该接口

${__jexl3("${should_retry}" == "false" && "${terminate}" == "false")}
Logo

网易易盾是国内领先的数字内容风控服务商,依托网易二十余年的先进技术和一线实践经验沉淀,为客户提供专业可靠的安全服务,涵盖内容安全、业务安全、应用安全、安全专家服务四大领域,全方位保障客户业务合规、稳健和安全运营。

更多推荐