带签名时间戳接口的重放与压力测试实战,用动态值在发送前现算签名

接口带签名怎么重放和压测?一次讲清楚动态签名方案

抓到的请求一重放就 401,是不是很熟悉?

做接口测试的人大概率遇到过这个场景:抓包工具里好不容易逮到一条关键请求,右键"编辑并重发",或者干脆导入到 Postman、Apifox 里改几个参数再发一次------结果服务端直接回一个 401 或 403,body 里可能还带一句 signature invalidtimestamp expired

第一反应通常是怀疑自己改错了什么,逐字段比对半天,发现请求头、body、URL 跟抓到的那条一模一样,还是不行。

问题往往不在于你改了什么,而在于你什么都没改,但时间已经变了 。这类接口在设计时就没打算被原样重放:请求里往往带着一个 timestamp(或 nonce)字段,以及一个基于密钥和请求内容算出来的 sign 字段,服务端会校验签名是否匹配、时间戳是否还在有效期内。抓包发生在 10 分钟前,你现在重放,时间戳早过期了;就算改了时间戳,签名又跟新时间戳对不上------这是一个"改一处、动全身"的联动结构,普通的"复制粘贴再发一次"打法在这里天然走不通。

压测场景更麻烦。批量重放好歹还能手动改一次时间戳凑合用,压测动辄每秒几百上千次请求,不可能每次发送前手工算一遍签名。如果压测工具不支持在发送前动态生成签名,那这类接口基本就没法用常规方式测吞吐和延迟。

这篇文章要讲的,就是怎么用"动态值"机制解决这个问题------让抓包记录 批量重放、会话重放 循环测试,以及针对签名接口压测,都能正常跑起来,而不用每次手动算 HMAC。

为什么带签名/时间戳的接口不能简单重放?

先把原理说清楚,后面的操作才有依据。

签名机制的核心思路是:把请求的关键内容(通常包括路径、部分参数、body、时间戳、有时还有一个随机数 nonce)和一个只有客户端和服务端知道的密钥(secret key)一起,喂给一个哈希算法(最常见的是 HMAC-SHA256,也有用 MD5、SHA1 的),算出一段签名字符串,附加在请求头或参数里发出去。服务端收到请求后,用同样的算法、同样的密钥,按同样的规则重新算一遍签名,跟你传过来的签名做比对,一致才放行。

这套机制天然带着两把锁:

  • 内容锁:签名的输入包含了请求内容本身。只要 body、时间戳、某个参数变了,理论上签名就该跟着变。你原样重放请求内容不变,签名倒是能对上,但......
  • 时效锁:时间戳是签名输入的一部分,服务端通常还会额外校验"这个时间戳跟服务器当前时间差是不是在允许范围内(比如 5 分钟)",超时直接拒绝,跟签名对不对无关。这一条卡死了"原样重放"这条路------就算签名字符串完全没变,时间戳一旦过期,请求照样被拒。

所以,想让这类接口重放成功,本质上要解决一个"联动重算"的问题:每次发送前,要用最新的时间戳,重新按服务端约定的规则算一遍签名,再把新时间戳和新签名一起塞进请求里发出去。 这件事如果靠人工,做一次两次还行,批量重放、循环测试、压力测试这种场景下完全不现实------这正是普通重放/压测工具直接用不了的根本原因,也是本文要解决的核心问题。

动态值机制详解:用 ${...} 语法把签名逻辑写进请求里

解决思路并不复杂:既然签名是"基于当前内容和当前时间现算出来的",那就让请求构造工具在发送前的最后一刻,把时间戳和签名都现算一遍,而不是发送一个写死的旧值。

抓包鹰(Trace Eagle)的请求构造器(Composer)里,任意字段------URL、Query 参数、请求头、请求体------都可以写 ${...} 形式的动态值表达式,发送前由内置引擎统一求值替换成真实内容。常用的动态值如下表:

动态值语法 作用
${uuid} / ${randomUUID} 生成一个随机 UUID
${timestamp} 当前 Unix 时间戳(秒级)
${timestampMs} / ${now} 当前 Unix 时间戳(毫秒级)
${random} 16 位随机十六进制字符串
${randomInt:min,max} 指定区间内的随机整数
${base64:内容} Base64 编码
${base64url:内容} Base64 URL-Safe 编码
${base64decode:内容} Base64 解码
${md5:内容} MD5 哈希
${sha1:内容} SHA1 哈希
${sha256:内容} SHA256 哈希
${hmacSha256:key,msg} HMAC-SHA256 签名(十六进制输出)
${hmacSha256Base64:key,msg} HMAC-SHA256 签名(Base64 输出)
${hmacSha1:key,msg} HMAC-SHA1 签名
${urlEncode:内容} URL 编码
${upper:内容} / ${lower:内容} 大小写转换

关键能力在于支持嵌套:动态值内部可以再引用环境变量,也可以互相组合。这就是 hmac sha256 签名 请求构造 场景里最常用的一行:

复制代码
${hmacSha256:${secret},${timestamp}${body}}

拆开看这行表达式在做什么:${secret} 是环境变量里存的密钥(不写死在请求里,环境切换时自动换);${timestamp} 是发送前现取的当前秒级时间戳;两者拼接后交给 hmacSha256 现算一遍签名。整个过程和真实客户端在代码里做的事情一模一样,只是不用写代码,写在字段里就行。

如果服务端要求的是"先取时间戳、再和 body 做 SHA256、再对这个结果做 HMAC"这种多层嵌套逻辑,同样可以一层套一层地写,比如:

复制代码
${hmacSha256:${secret},${sha256:${body}}${timestamp}}

引擎会按照从内到外的顺序依次求值。另外有个细节值得一提:如果某个动态值写错了(比如变量名拼错、环境里没定义对应变量),它会原样保留在请求里 ,不会报错也不会替换成空值。这带来一个直观的好处------发送前面板里能看到最终真正发出去的内容,哪个变量没求值成功,一眼就能看出来(还留着 ${xxx} 字样),不像有些工具遇到未定义变量要么直接报错中断,要么悄悄发出一个空字符串,出了问题还得慢慢排查是哪个环节。

手把手操作:把一条抓到的签名请求改造成能重放的动态值请求

以下以一条典型的签名请求为例,走一遍完整流程。假设抓到的原始请求长这样:

复制代码
POST /api/v1/order/create
Host: api.example.com
X-Timestamp: 1723190400
X-Sign: 8f14e45fceea167a5a36dedd4bea2543...
Content-Type: application/json

{"userId":"1001","amount":99.5}

第一步:把请求载入构造器。 在流量列表里找到这条请求,右键选择"编辑并重发"(或在详情面板点同名按钮),请求会原样载入 Composer------地址按抓包时的协议/主机/端口自动还原,ConnectionContent-Length 这类无关连接头会自动剔除,body 也会按内容类型自动识别好,不用你手动重新拼一遍。

第二步:把密钥存进环境变量。 打开环境管理,新建或编辑一个环境(比如"测试环境"),加一个变量 secret,值填入分析得到的签名密钥。这一步的好处是密钥不会散落在每条请求里,换环境(比如测试环境切生产环境用不同密钥)时只需要切环境,不用逐条改请求。

第三步:把写死的时间戳换成动态值。 把请求头里的 X-Timestamp: 1723190400 改成:

复制代码
X-Timestamp: ${timestamp}

第四步:把写死的签名换成 HMAC 表达式。 根据分析出来的签名规则(这一步通常需要提前搞清楚服务端签名规则是"时间戳+body"还是别的拼接方式,可以参考接口文档或对多组抓包样本做比对),把 X-Sign 字段改成对应的动态值表达式,例如:

复制代码
X-Sign: ${hmacSha256:${secret},${timestamp}${body}}

第五步:点发送,检查结果。 发送前,面板会展示求值后的真实请求内容------确认 X-Timestamp 变成了当前时间戳,X-Sign 也变成了一串新的十六进制签名,而不是残留的 ${...} 字样。发出去,如果服务端返回正常业务数据而不是 401/403,说明签名规则还原对了。

改造完成后,这条请求就可以反复点发送,每次都会用最新时间戳现算签名,不再受时效限制。如果只是临时调试单条请求,到这一步已经够用;如果是要批量重放一串接口或者压测,接下来两节是重点。

会话重放实战:批量重放一串带签名的接口做流程回归

单条请求能重放之后,很多场景需要的其实是"一串"------比如一次完整的登录、下单、支付流程,涉及好几个接口,每次改完代码想验证整个链路还通不通,这就是会话重放 循环测试要解决的问题。

操作方式:

  1. 从抓包播种。 在流量列表里勾选多条相关请求,右键"重放选中的请求"(单条也可以用"重放此请求"),这些请求会按抓包时的先后顺序批量载入一个会话重放任务;也可以手动逐条添加。
  2. 确认动态值已经改造到位。 如果这几条请求都带签名,按上一节的方法逐条把时间戳和签名字段换成 ${timestamp}${hmacSha256:...} 这类表达式------由于同一套环境变量(比如 secret)在所有请求里通用,改起来比想象中快。
  3. 配置循环参数。 可以设置循环次数(比如跑 10 轮验证稳定性)、每条请求之间的间隔时间、以及需要切换的环境。
  4. 开始执行,看结果。 任务会按顺序逐条把请求发出去,每条都实时展示方法、URL、状态码、延迟,同时给出整体进度、成功/失败计数和总耗时。

这套流程的价值在于:因为动态值是发送前现求值,会话里的每一条请求、每一轮循环,时间戳和签名都是当次现算的,不会出现"第一轮成功、第二轮全部超时失效"的情况。对于需要频繁跑回归的业务流程(比如一次改动之后想确认下单链路没坏),这比每次手动改参数重放快得多。

压力测试实战:右键压测一个带签名的接口

抓到接口只是第一步,很多时候更关心的是这个接口扛不扛得住------签名接口压测 恰恰是最容易被现有工具卡住的场景:压测工具如果不支持动态生成签名,面对这类接口基本无从下手。

两种入口

  • 从抓包记录直接压测:在流量列表里右键"压测此请求",方法、地址、请求头、请求体会自动带入压测配置,改好动态值表达式(跟前面单条请求改造的方法一致)之后就能立即开始。
  • 手动配置:从工具箱打开压测面板,浮窗形式呈现,可以同时开多个窗口,跑多个独立的压测任务互不干扰------比如一边压测下单接口,一边压测查询接口,两个窗口的统计数据是分开的。

恒定QPS压测 vs 并发压测,怎么选?

压测面板提供两种压力模式,很多人一开始分不清该用哪个:

  • 恒定速率(QPS)模式:按设定的"每秒请求数"均匀发压,比如设置 200 req/s,请求会按固定节奏平均发出,而不是一股脑挤在一起。这种模式采用更严谨的延迟统计口径------即使在高负载、服务端出现排队的情况下,p99 这类尾延迟依然反映真实情况,不会因为统计口径问题被"稀释"。适合回答"这个接口在 200 QPS 这个具体负载水位下,真实延迟表现如何"这类问题,更贴近线上真实流量画像。
  • 并发模式:固定并发数(连接数),背靠背连续发送,不设速率上限,用来测这个接口在没有速率限制的情况下极限能扛多少吞吐。适合回答"这个接口最大能撑多少 QPS、瓶颈在哪"这类问题。

简单记忆:想知道"在某个具体流量水位下延迟怎么样",用恒定 QPS;想知道"这个接口的天花板在哪",用并发拉满去打。 两种模式都支持配置请求方法/URL/Query/Header/Body、速率(1-100000 req/s)、并发/连接数(1-2000)、持续时长(1-3600 秒),高级选项里还能控制是否跟随重定向、是否跳过 TLS 校验,连接层面自动复用 keep-alive,HTTPS 目标会自动尝试 HTTP/2。

怎么看 p50-p99.9 延迟分位判断接口健康度

压测过程中实时展示的核心数据包括:

  • 吞吐:实时 RPS 数字 + 折线图,直观看到发压节奏是否稳定。
  • 计数:已发送、成功、错误、已用时。
  • 延迟分位:p50 / p90 / p95 / p99 / p99.9 / 最大值,单位毫秒。
  • 状态码分布:2xx / 3xx / 4xx / 5xx / 错误分别统计。

这里重点说一下为什么要看 p99 而不只看平均延迟(也是后面 FAQ 会展开的问题):平均延迟很容易被大多数正常请求"拉平",掩盖掉少数慢请求的存在。举个例子,1000 次请求里 990 次都是 20ms,10 次因为某种原因卡到 2000ms,平均延迟可能看起来还是 40 多毫秒、挺正常,但这 10 次慢请求如果对应到真实用户身上,就是 1%的用户等了 2 秒------这在平均值上完全看不出来,但在 p99 上会清清楚楚地体现。所以判断接口健康度,通常的做法是:p50 看典型体验,p95/p99 看长尾体验,p99.9 和最大值看极端情况,四个数字放在一起看,比单独看一个平均延迟靠谱得多。

对签名接口而言,压测的意义还多一层:可以顺带验证签名/时间戳校验本身有没有成为性能瓶颈------如果服务端每次请求都要做一次签名校验计算,理论上会比无签名接口多一点开销,通过压测数据能直观看到这部分开销是否在可接受范围内。

常见问题 FAQ

Q1:动态值表达式写错了,或者变量没定义,会发生什么?

不会报错中断,也不会发出空值------引擎会把无法解析的 ${...} 原样保留在最终请求里。发送前的预览面板能直接看到这一点,比如本该是一串十六进制签名的位置,还显示着 ${hmacSha256:...} 字样,说明求值失败,可以照着字样定位是变量名错了还是环境没切对。

Q2:多个压测窗口同时跑,会不会互相干扰?

不会。压测面板支持同时打开多个独立窗口,各自维护自己的统计数据(吞吐、延迟分位、状态码分布互不共享),可以放心同时压测多个不同的接口,或者用不同参数对同一个接口做对比测试。

Q3:为什么要关注 p99 而不只看平均延迟?

平均延迟会被大多数正常请求"拉平",掩盖少数慢请求。p99 反映的是"最慢的 1%请求有多慢",这部分往往对应真实场景里体验最差的那批用户请求,是判断服务是否存在长尾问题的关键指标,具体解释见上一节。

Q4:高负载下 p99 数字会不会"虚低"?

这是压测工具设计上一个容易被忽视的细节:如果只统计"已经完成的请求"的延迟,在高负载、服务端排队严重的情况下,那些还没处理完的慢请求根本没被计入统计,算出来的 p99 反而会显得比实际情况更好看,造成"看起来还行,其实已经在积压"的误判。恒定 QPS 模式下采用了更严谨的统计口径来规避这个问题,让延迟分位数在高负载下也能如实反映真实情况,这也是恒定 QPS 模式相比单纯"拼命发"更适合评估真实容量的原因之一。

Q5:环境变量里的密钥换了(比如测试环境和生产环境密钥不一样),要改所有请求吗?

不需要。密钥存在环境变量里,请求里引用的是 ${secret} 这个变量名而不是具体值,切换环境时变量值自动跟着换,所有引用了这个变量的请求(包括会话重放和压测里的请求)都会自动使用当前环境对应的密钥,不用逐条改。

Q6:改造好的带动态值的请求,能不能导出给团队其他人用,或者用其他语言调用?

可以从 cURL 导入现成的请求(自动解析 -X/-H/-d/--data-*/-u/-b/-A/-e 等常见参数),也可以一键把当前请求导出为 cURL、Python、JavaScript(fetch)、Go、OkHttp 代码片段。不过需要注意,导出的代码片段是"求值前"的静态快照,动态值和 HMAC 签名的现算逻辑需要在导出后自行用目标语言实现------这一点跟直接在工具里用动态值实时重放是两回事。工作区本身保存为纯文本文件,可以用版本管理工具做 diff 和 review,方便团队协作维护这套请求集合。

与 Postman、JMeter 等工具的客观对比

签名接口的重放和压测不是新问题,业内已有的工具各自有应对方式,值得客观说一下门槛差异:

  • Postman / Apifox :这类接口调试工具通常提供"预请求脚本"(Pre-request Script)机制,可以在发送前执行一段 JavaScript 代码,动态计算签名、时间戳等字段,功能上足以覆盖签名场景。差异主要在使用门槛上------预请求脚本需要写代码,涉及 JS 语法、库的引入方式、调试脚本报错等,对不熟悉前端脚本编写的测试人员有一定学习成本;相比之下,${hmacSha256:key,msg} 这种内联表达式语法更接近"填空",不需要理解脚本执行环境和 API,上手门槛更低,但也意味着可定制的处理逻辑不如通用脚本语言灵活------遇到特别复杂的、多步骤条件判断的签名规则,脚本方式的表达能力会更强。
  • JMeter、ab、wrk、k6 等专业压测工具:这些工具在压测能力本身(并发模型、脚本化、分布式压测、报表体系)上非常成熟,功能边界比一般抓包工具自带的压测能力更广。但用来测签名接口时,通常需要额外配置------比如 JMeter 要写 BeanShell 或 Groovy 前置处理器来现算签名,且这些工具原生不带"从抓包记录直接转成压测任务"的能力,一般需要先把抓到的请求参数(URL、Header、Body 结构)手动搬到压测工具里配置一遍,再补上签名生成逻辑,才能开始压测。对已经很熟悉 JMeter 脚本体系、或者需要压测集群分布式发压、需要对接现有 CI/CD 报表体系的团队,这些工具依然是更合适的选择。
  • 图形化一体工具(比如抓包鹰):把"抓到请求 → 右键压测/重放 → 改几个动态值表达式 → 直接开跑"压缩成几步操作,不需要写脚本、不需要手动搬运参数,签名和时间戳作为动态值语法的一部分内联在字段里。这条路径的价值在于把"抓包→改造→测试"这套流程的操作门槛降到最低,适合日常调试、接口自测、中小规模压测场景;但如果需要的是大规模分布式压测、或者要接入现有的持续集成报表体系,JMeter、k6 这类专业压测工具的生态更完整。

三类工具不是互斥关系:用抓包鹰这类工具快速验证签名规则、跑通单接口或小规模压测,摸清楚接口的大致延迟水位和瓶颈之后,如果需要更大规模、更工程化的压测,再迁移到 JMeter/k6 这类专业压测体系,是一条比较顺的路径。

小结

带签名/时间戳校验的接口不能被原样重放,根源在于签名和时间戳把请求内容与时效绑在了一起,改一处就要联动重算全部------这不是抓包工具或压测工具的 bug,而是这类接口本身的设计目的。解决思路也不复杂:在发送前的最后一刻,用动态值表达式现取时间戳、现算签名,而不是发一个写死的旧值。

具体到操作上:单条请求用 ${timestamp}${hmacSha256:key,msg} 这类内联表达式改造字段,配合环境变量存放密钥;一串接口的流程回归交给会话重放,循环次数和间隔都能配置;关心接口扛不扛得住就用压力测试,恒定 QPS 模式看真实负载下的延迟表现,并发模式测极限吞吐,p50/p95/p99/p99.9 几个分位数放在一起看,比只看平均延迟靠谱得多。

抓包鹰(Trace Eagle)把这套"抓包记录 批量重放"和"右键压测此请求"的流程做成了图形化操作,动态值语法覆盖了签名接口重放和压测最常用的场景,免费、跨平台,主打"抓得到,解得开,看得懂"------对于不想为了测一个签名接口去写预请求脚本或配置 BeanShell 处理器的场景,是一个可以尝试的选项。如果你的场景本身已经深度依赖 Postman 的脚本生态,或者需要 JMeter/k6 级别的大规模压测能力,这些专业工具依然值得继续使用,选择哪种方式,取决于你要解决的问题规模和团队已有的技术栈。

相关推荐
TechWayfarer1 小时前
批量查IP归属地,在线API、脚本、离线库怎么选?三种方案实测对比
网络·python·网络协议·tcp/ip
ryan_9965 小时前
MCP Transport 完整指南:stdio 与 Streamable HTTP 到底怎样传消息
网络·网络协议·http
笨鸟先飞,勤能补拙8 小时前
AI与深度学习:从原理到应用
人工智能·windows·vscode·深度学习·机器学习·网络安全·github
数据知道8 小时前
网络安全实战:子域名接管实战——从 CNAME 配置错误到完全控制
网络·安全·web安全·网络安全
小新讲网安8 小时前
云安全攻防实战:AWS与阿里云渗透测试全流程指南
安全·web安全·网络安全·阿里云·自动化·云计算·aws
LayZhangStrive9 小时前
后端通识 - 远程服务调用RPC
网络·网络协议·rpc·sentinel·openfeign·远程服务调用
zyf10441610 小时前
暑期实践日志 Day29:检核视频导出,准备素材上交
学习·计算机网络·剪辑·暑期实践·课题任务
华清远见成都中心10 小时前
FreeRTOS事件组(Event Group)的工作机制分析
服务器·网络·网络协议
tiantianuser11 小时前
NVME-oF IP 设计15 : 适于高速网络存储系统的IP设计1
网络协议·rdma·高速传输·cmac·roce v2