在一次使用 Jmeter 做接口性能测试时,发现项目的登录接口,前端传参时,对密码进行了加密后传参,使用密码明文登录不了。当时想了两个方案解决这个问题:
方案一:打开浏览器,按下 F12,使用一个用户,输入正确的秘密从界面登录,查看登录接口传入的密码密文。然后在 Jmeter 中登录接口的 password 参数值设置为此密文。
不足:Jmeter 启动多个线程时,每个登录用户的密码需一样
方案二:找前端开发询问密码加密的方式,然后在 Jmeter 上编写 js 脚本,按加密秘钥加密密码明文得到密文作为变量。然后登录接口引用变量。
当时由于时间紧迫,且周日,开发不在公司,采用了第一种方案。后面有时间,尝试了第二种方案。以下记录第二种方案的过程。
步骤 1:首先找前端开发询问了登录接口密码加密的代码和引用的包 jsencrypt.js 文件。然后将 js 文件放到 jmeter 启动 jmeter.bat 目录下。前端代码如下
const getEncrypt = (data) => {
let publickKey = '公钥字符串'
let crypt = new JSEncrypt()
crypt.setPublicKey(publickKey)
return crypt.encrypt(data)
}
步骤 2:打开 Jmeter,在登录接口下方添加 JSR223 PreProcessor。编写脚本如下:
load("jsencrypt.js"); //加载js文件
var publicKey = "公钥字符串"; //找开发要的公钥秘钥
var crypt = new JSEncrypt();
crypt.setPublicKey(publicKey);//设置公钥秘钥
log.info("${passwd}"); //passwd是通过csv获取到的参数值,打印出密码明文,用于调试
var passwdEncryption = crypt.encrypt("${passwd}"); //对密码明文进行加密
log.info(passwdEncryption); //打印出秘密加密后样式
vars.put("passwdEncryption", passwdEncryption); //把加密后的放入全局变量中,然后在登录接口引用${passwdEncryption}
Jmeter 截图如下
步骤 3:执行 Jmeter 脚本,查看结果树,发现登录失败,提示账号或密码错误。打开 Jmeter 的日志,出现 navigator is not defined in jsencrypt.js 错误信息。
因为 jsencrypt.js 文件里面使用到了 navigator 变量。这个变量在浏览器作为全局变量存在,但在 Jmeter 上没有,所以出现了未定义问题。
首先想的是 Jmeter 没有,同时这个变量也用不到,所以就把 jsencrypt.js 中相关 navigator 的代码注释掉,如下图。再次执行 Jmeter 脚本,报 window 也未定义,所以关于 window 代码也注释掉。
navigator 和 window 变量相关代码都注释后,再执行 Jmeter 脚本。报了其他问题 JSEncrypt is not a function...。
步骤 4:感觉注释掉 navigator 和 window 变量行不通,就继续在网上寻找资料,发现通过在 Jmeter 中添加var window = this; var navigator = this;
定义 window 和 navigator 变量。不用修改 js 文件,能实现密码加密和登录成功。
在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。单独使用 this,会指向全局 (Global) 对象。我理解的是会获取 jmeter 环境下的全局对象。最终脚本如下
var window = this; //定义window全局变量
var navigator = this; //定义navigator全局变量
load("jsencrypt.js"); //加载js文件
var publicKey = "公钥字符串"; //找开发要的公钥秘钥
var crypt = new JSEncrypt();
crypt.setPublicKey(publicKey);//设置公钥秘钥
log.info("${passwd}"); //passwd是通过csv获取到的参数值,打印出密码明文,用于调试
var passwdEncryption = crypt.encrypt("${passwd}"); //对密码明文进行加密
log.info(passwdEncryption); //打印出密码加密后样式
vars.put("passwdEncryption", passwdEncryption); //把加密后的放入Jmeter全局变量中,然后在登录接口引用${passwdEncryption}
附录:
在脚本log.info("${passwd}"); var passwdEncryption = crypt.encrypt("${passwd}");
中 passwd 是通过 csv 获取到的参数值,需要加上双引号,使参数值变为字符串类型,不然因为特殊字符(@)会报如下错误。
或者没有特殊字符(@),也会出现引用未定义