使用jMeter构造逻辑上有依赖关系的一系列并发请求

41次阅读

共计 4016 个字符,预计需要花费 11 分钟才能阅读完成。

相信前端开发工程师对 CSRF(Cross-site request forgery)跨站请求伪造这个概念都非常熟悉,有的时候也简写成 XSRF,是一种对网站的恶意利用。
尽管听起来像跨站脚本(XSS),但它与 XSS 非常不同,XSS 利用站点内的信任用户,而 CSRF 则通过伪装成受信任用户的请求来利用受信任的网站。

CSRF 攻击的防御方式有多种,最简单最易实现的一种思路就是在客户端向服务器发起的请求中放入攻击者无法伪造的信息,并且该信息没有存储于 cookie 之中。技术上来说,当客户端向服务器发起请求执行一些敏感操作之前 (比如用 HTTP post 实现的转账,扣款等功能),服务器端随机产生一个 token,返回给客户端。客户端接下来的操作,必须在 HTTP 请求中以参数的形式把这个服务器端颁发的 token 带上。同时服务器端在实现给客户端分配 token 的同时,也要加入一个 token 校验机制。如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。这个 token 我们一般称为 CSRF token。
讲了这么多,是为了引入本文想要讨论的话题。假设我想用 jMeter 测试一个 OOdata 服务创建 Service Ticket 的性能。因为创建功能不像读操作,执行之后会对系统产生持久化影响 (Persistence side-effect), 因此服务器端的实现加入了 CSRF token 的校验。这就是说,如果我们直接用 jMeter 构造并发的 HTTP post 请求,是没有办法完成测试的,这些请求因为没有包含 CSRF token,会被服务器端直接拒绝掉。
根据前面描述的 CSRF 攻防原理,CSRF token 是服务器端随机生成的,客户端无法用任何技术进行伪造,因为为了测试接口 HTTP post 操作进行 Service Ticket 的创建,我们必须构造一个它的前置 HTTP GET 请求,专门用于得到服务器返回的 CSRF token,然后再构造真正用于性能测试的 HTTP POST 请求,把第一步 GET 请求获得的 CSRF token 附到 POST 请求的头部中去。
本文介绍在 jMeter 里如何维护并配置这种具有依赖关系的一组请求。
当然如果您不喜欢用 jMeter,想自己写代码实现,也是可以的。可以参考我放在 github 上的 Java 代码实现。
用 jMeter 的好处是不需要编程,通过简单的配置就能实现这个性能测试需求,一般没有开发背景的测试人员也能独立完成。
First let us have a look how JMeter could archive the same without even one line of programming.
My project in JMeter is displayed with the following hierarchy. I have configured with“Number of 5 threads”in my thread group, so once executed, the response time of these 5 threads are displayed in result table together with average response time.
从下图能看出,因为拿 CSRF token 的 HTTP GET 在逻辑上必须先于实际需要测试性能的 HTTP POST 请求,这实际上构成了一个 Transaction- 事务,所以我使用 jMeter 里提供的 Transaction Controller 来管理。

Some key points for this JMeter project creation
(1) Since now one thread should cover both XSRF token fetch via HTTP get and Service request creation via HTTP post, so a transaction controller is necessary to include both request.

(2) Create the first HTTP request to fetch XSRF token. The setting could be found below: adding a http header field with name asx-csrf-token and value as“fetch”:
在 HTTP GET 请求的头部加上一个名为 x -csrf-token 的字段,值赋成 fetch。这样服务器接到这个请求,就知道这是客户端发起的 CSRF token 请求,于是服务器响应这个请求,把创建好的随机 CSRF token 通过 HTTP response 头部字段的方式返回给客户端。

下一个问题就是,服务器返回给客户端合法的 CSRF token 后,jMeter 如何读取到这个 token,并用于接下来的请求?
幸运的是,jMeter 提供了正则表达式提取式,可以让我们很方便地从 HTTP 响应结构中提取出 token 来。
Create a Regular Expression Extractor to parse the XSRF token from response header and stored it to a variable named“jerrycsrftoken”.
下图构造了一个 jMeter 正则表达式提取器,工作于 HTTP 响应的头部字段,解析出的 token 值存储于变量 jerrycsrftoken 中。

Before you continue, please make sure that the XSRF token is correctly parsed from request header, which could be confirmed by printing it out in a debug sample:
这个请求构造完之后,我们先试着运行一次,确保在变量 jerrycsrftoken 里确实看到解析好的 CSRF token。

(3) Create another HTTP request with type POST.
这时万事俱备,我们可以开始构造真正要进行性能测试的 HTTP post,即 Service Ticket 的创建请求了。

请求的报文正文:Just paste the following text to the tab“Body Data”:
–batch_1
Content-Type: multipart/mixed; boundary=changeset_1

–changeset_1
Content-Type: application/http
Content-Transfer-Encoding: binary

POST ServiceRequestCollection HTTP/1.1
Content-Length: 5000
Accept: application/json
Content-Type: application/json

{
“ServicePriorityCode”: “2”,
“Name”: {“content”: “Jerry Testing ticket creation via JMeter ${uuid} “},
“ServiceRequestDescription”: [
{
“Text”: “Piston Rattling 1 – Generic OData Test Create”,
“TypeCode”: “10004”
},
{
“Text”: “Piston Rattling 2 – Generic OData Test Create”,
“TypeCode”: “10007”
}
]
}
–changeset_1–

–batch_1–
In the body text I use a user-defined variable ${uuid} which we could create it in last step. And for this post request, use the XSRF token fetched from previous HTTP get request.
前面说过,POST 请求的头部需要加上合法的 CSRF token,此处我们使用前面 GET 请求已经拿到的并且存储于变量 jerrycsrftoken 中的 token 值:

我希望最后通过并发测试生成的 Service Ticket 的描述信息的后缀是 1 到 100 的随机正整数,因此我使用 jMeter 里自带的一个随机数发生器:
(4) As the last step, create a user variable by using JMeter built-in function __Random, to create a random number between 1 ~ 100 as a fragment of created Service Request description.

Now execute the Thread group, and the execution detail for these three HTTP request could be reviewed separately in tree view:
试着运行一下,发现这个 POST 操作确实按照我们期望的那样,在 HTTP 头部字段里加上了正确合法的 CSRF token:

For example, the XSRF token is successfully fetched in the first request: rdPy7zNj_uKDYvQLgfQCFA==And used as one header field in second HTTP Post request as expected:

And finally in UI we could find the created Service request with random number between 1 ~ 100 as postfix:
在 UI 上观测到我构造的 5 个并发请求创建的 Service Ticket,说明 CSRF token 在服务器端的校验成功,同时发现描述信息都带上了随机数,说明我的 jMeter 随机数生成器的用法也正确。

希望本文对大家的工作有所帮助。
要获取更多 Jerry 的原创文章,请关注公众号 ” 汪子熙 ”:

正文完
 0