Jmeter 440 错误代码。我该如何解决这个错误?

Jmeter 440 error code. How can I resolve this error?

我正在使用 Jmeter 和 Blazemeter 为使用 Genexus 制作的网站创建登录脚本。 我遇到的问题是 POST。

每当我尝试制作一个 POST http 请愿书时,Jmeter 会抛出以下内容:

如您所见,在响应正文中,我有一个 440 http 错误代码。这是登录超时,表示客户端会话已过期,必须重新登录。我以前有一个 403 错误代码,但现在,经过一些安排,我有 440。你对如何解决这个问题有什么建议吗?

任何 HTTP Status 4xx 都是 客户端错误 ,也就是说您发送的请求不正确。

如果自定义 440 http status code means "session has expired" my expectation is that you have a recorded hard-coded session ID somewhere in your request parameters or headers

您应该仔细检查以前的回复,并在找到后寻找似乎是会话 ID 的内容 - extract it using a suitable JMeter's Post-Processor and replace hard-coded session ID with the appropriate JMeter Variable. The process is known as correlation

首先,我不是 Genexus 方面的专家。我所有的发现都是从黑盒的角度出发的。

Genexus 安全

我发现 Genexus 至少需要两件事才能在 Web 应用程序上进行身份验证(我只测试了 Java 和 .Net 生成的应用程序)。

  1. GXState参数。此参数在 post 请求中发送,根据我的理解,它作为“同步器令牌模式”工作,请参阅 Cross-site request forgery 上的更多信息。我们需要在每个 post 请求中发送此参数。

  2. gxajaxEvt参数。这是 Genexus Apps 特有的。在 documentation mentions this parameter is send encrypted in the URL, and this behavior is managed by the "Javascript debug mode property":

# Javascript Debug Mode: Yes
http://{server}:{port}/{webappname}/servlet/com.{kbname}.{objectname}?gxfullajaxEvt,gx-no-cache=1442811265833
# Javascript Debug Mode: No (default value)
http://{server}:{port}/{webappname}/servlet/com.{kbname}.{objectname}?64df96a2d9b8480aed416e470dae529e,gx-no-cache=1442811265833

JMeter 脚本

  1. 所以,要得到GXState,我们可以使用Regular Expression Extractor

    创建变量的名称:GXState

    正则表达式:name="GXState" value='(.*?)'

    模板:$

    匹配号:1

    默认值:NOT_FOUND

  2. GXState是一个JSON对象,从中我们可以提取GX_AJAX_KEY来加密gxajaxEvt字符串。请注意,我发现 GX_AJAX_KEY 是在这种情况下用于加密的密钥,但其他一些也适用。我们可以使用 Browser Web Console 调试它,使用这个:

gx.sec.encrypt("gxajaxEvt")

我们会看到这样的东西: "8722e2ea52fd44f599d35d1534485d8e206d507a46070a816ca7fcdbe812b0ad"

我们可以发现,所有的客户端加密代码都在gxgral.js文件中。 Genexus 使用块大小为 128 位的 Rijndael 算法(AES 的子集)。

要在 JMeter 脚本中模拟此客户端行为,我们可以使用“JSR 233 采样器”。获得 Rijndael 结果的一种方法是使用 Bouncy Castle 库。我们需要将这个 jar (bouncycastle:bcprov-jdk15to18:1.68) 添加到 JMeter 的 lib 文件夹中才能使用它。

我们的代码脚本将是这样的(语言 Groovy 3.0.5/Groovy 脚本引擎 2.0):

import com.jayway.jsonpath.JsonPath
import java.nio.charset.StandardCharsets
import java.util.Arrays
import org.bouncycastle.crypto.BufferedBlockCipher
import org.bouncycastle.crypto.InvalidCipherTextException
import org.bouncycastle.crypto.engines.RijndaelEngine
import org.bouncycastle.crypto.params.KeyParameter
import org.bouncycastle.util.encoders.Hex
import org.apache.jmeter.threads.JMeterContextService
import org.apache.jmeter.threads.JMeterContext
import org.apache.jmeter.threads.JMeterVariables

String gxState = vars.get('GXState')
String gxAjaxKey = JsonPath.read(gxState,'$.GX_AJAX_KEY')
byte[] input = Arrays.copyOf('gxajaxEvt'.getBytes(StandardCharsets.UTF_8), 16)
RijndaelEngine engine = new RijndaelEngine(128)
KeyParameter key = new KeyParameter(Hex.decode(gxAjaxKey))
BufferedBlockCipher cipher = new BufferedBlockCipher(engine)
cipher.init(true, key)
byte[] out = new byte[16]
int length = cipher.processBytes(input, 0, 16, out, 0)
cipher.doFinal(out, length)
String encryptedOutput= Hex.toHexString(out)
log.info 'gx.sec.encrypt("gxajaxEvt")='+encryptedOutput
String gxNoCache = String.valueOf(System.currentTimeMillis())
log.info 'gx-no-cache='+gxNoCache
vars.put('gxajaxEvt', encryptedOutput)
vars.put('gxNoCache', gxNoCache)

脚本是这样工作的:

  • 首先,我们提取之前的GXState变量。
  • 其次,使用 JSON 路径(已经 available in JMeter 5.4.1)提取 GX_AJAX_KEY 属性.
  • 第三,我们将 Rijndael 算法应用于 gxajaxEvt,使用 GX_AJAX_KEY 作为密钥。
  • 我们还创建了 gx-no-cache 来处理缓存。

  1. 使用这些变量我们可以成功发送下一个请求:

我们可以找到此示例 JMeter 脚本可用 here

复杂脚本请参考this guide(需要GXTest)

如果我们在 JMeter 中遇到此异常 (java.util.zip.ZipException: Not in GZIP format),请也参考此 answer