使用Apifox动态生成请求参数
比如,我要调试一个登录请求,使用Apifox来进行调试,登录请求需要一个密码,这个密码需要进行AES加密后才能发送出去,我希望在Apifox中我填入明文密码,然后Apifox帮我进行AES加密后再发送出去。
我的登录使用的是Kotlin语言(属于Java语言),所以我希望用的Kotlin代码去加密,在IntelliJ中,创建一个使用gradle的Kotlin项目,截图如下:
然后在该项目中加入Aes加密的代码,项目结构如下:
Aes.kt
实现如下:
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
object Aes {
/** 使用AES进行加密,加密后的数据使用Base64编码为String */
fun encrypt(rawData: String): String = Base64.getEncoder().encodeToString(getCipher(Cipher.ENCRYPT_MODE).doFinal(rawData.toByteArray()))
/** 把AES加密并通过Base64编码的String进行解密,还原为原始的String */
fun decrypt(base64Data: String): String = String(getCipher(Cipher.DECRYPT_MODE).doFinal(Base64.getDecoder().decode(base64Data)))
private fun getCipher(mode: Int) = Cipher.getInstance("AES/ECB/PKCS5Padding").apply { init(mode, SecretKeySpec("99cdefgabcdefg88".toByteArray(), "AES")) }
}
Main.kt
实现如下:
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("无运行参数")
return
}
selectAction(args)
}
fun selectAction(args: Array<String>) {
val action = args[0]
when (action) {
"aes" -> aes(args)
}
}
fun aes(args: Array<String>) {
val data = args[1]
val encrypt = Aes.encrypt(data)
println(encrypt)
}
运行main
函数时传入两个参数,第一个参数传aes,则我们就会调用AES加密第二个参数,并把加密结果输出到控制台(这样设计是为了方便扩展,比如后面可能还有别的功能,比如生成检验码)。Apifox会读取我们的输出,所以它可以拿到加密后的内容。Apifox并不能直接执行kotlin或java源文件,它只能执行jar包,所以我们需要把kotlin项目打成一个可执行jar,在build.gradle.kts
中添加一个tasks.jar
的任务,如下:
plugins {
kotlin("jvm") version "1.9.23"
}
group = "cn.dazhou.api.util"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-test")
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(11)
}
tasks.jar {
archiveFileName.set("ApiUtil.jar")
manifest {
attributes["Main-Class"] = "cn.dazhou.api.util.MainKt"
}
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
}
如上代码,除了tasks.jar
这个任务代码,其它都是创建项目时自动生成的,archiveFileName
用于设置生成的jar的文件名,manifest
中设置了main
函数的类是哪一个类。
双击执行gradle
面板中的jar
任务即可生成一个可执行jar,截图如下:
生成的jar在build/libs
目录下:
打开Apifox,我们使用apifox官方提供的简单请求测试接口https://echo.apifox.com/post,我们设置为post请求,并添加一个password参数为123456,然后发送请求,结果如下:
如上图,可以看到响应结果中,服务器收到了明文的密码。
点击Apifox右上角的设置按钮,然后点击 “外部程序” ,在这里可以看到外部程序的目录,点击“打开目录”按钮,然后把我们的ApiUtil.jar
复制到这个目录中,如下图,
回到Apifox的快捷请求界面,点击 “前置操作”,然后添加一个自定义脚本,截图如下:
这是javascript
语言,一些具体逻辑不知道怎么写的可以直接问DeepSeek,上面代码功能应该也不用解释了,一看就懂了,功能就是读取post请求中urlencoded
中的password
参数,然后调用我们的ApiUtil.jar
进行加密,加密后把原来的password
参数移除,然后添加经过加密后的参数。再次发送请求,结果如下:
如上图,可以看到响应体中的密码就是加密后的密码了,后面再调试时,我们密码随便换,只需要填入明文密码,工具会自动帮我们加密。
另外,需要注意的是,如果请求参数使用的是form-data
类型,则代码中应该是以pm.request.body.formdata
来读取参数,如果使用的是x-www.form-urlencoded
类型,则是以pm.request.body.urlencoded
来读取参数。form-data
(即multipart/form-data
)和x-www-form-urlencoded
(即application/x-www-form-urlencoded
)是两种常见的表单数据编码方式,它们之间的区别可以直接问DeepSeek,简单来说,如果只传文本参数使用x-www-form-urlencoded
,如果要传文件或者文件加文本,则用form-data
。
前置操作中默认就一个前置操作,叫 “变量替换” 拖动左侧的6个点可以上下调整位置,如下图:
这个上下位置的顺序决定了它们的执行顺序,比如“变量替换”在上方,则Apifox会先执行变量替换,再执行我们的自定义脚本。假如“自定义脚本” 在上面,如果我们在自定义脚本中想读取替换后的变量,则会读取不到,因为自定义脚本运行时,变量替换的脚本还没有运行。具体可以查看官方文档,我当时问过Apifox交流群,给出的一些链接如下:
-
Apifox支持通过调用外部程序,调用在 “外部程序目录” 中的可执行代码文件。这些文件可以是:Java 程序归档文件(JAR 包)、其它编程语言的源代码文件、可执行脚本文件。
使用方式请参考帮助文档☞https://docs.apifox.com/5586304m0 -
在请求接口的过程中,通常需要将已引用的变量转换为真实的请求数据。某些高安全等级接口会要求请求数据需要加密 / 签名,便需要针对完整的请求数据进行签名处理,最后再将加密数据发送至服务端口。
为了解决接口加密 / 签名问题,可以在 Apifox 的「前置操作」环节中对整个请求数据进行签名处理以满足接口的安全要求。
注意:若“自定义脚本”放置在“变量替换”步骤之后,那么签名脚本可以获取到变量替换后的实际请求数据。否则只能获取到替换之前的数据
详细说明请参考☞ https://docs.apifox.com/5831263m0#%E8%BF%90%E8%A1%8C%E8%84%9A%E6%9C%AC
接口签名更多示例☞ https://docs.apifox.com/5802226m0
扩展:
把前面的示例扩展一下,比如登录时还需要传本机ip参数,在Kotlin项目中,我们添加一个Utils类,用于封装各种工具函数,比如获取IP地址的函数,代码如下:
object Utils {
fun getActiveIPv4Address(): String = try {
// 使用 Google 的公共 DNS 作为目标(无需实际连接)
Socket().use { socket ->
socket.connect(InetSocketAddress("8.8.8.8", 53), 1000)
val localAddress = socket.localAddress as? Inet4Address
socket.close()
localAddress?.hostAddress ?: ""
}
} catch (e: Exception) {
""
}
}
Main.kt
如下:
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("无运行参数")
return
}
selectAction(args)
}
fun selectAction(args: Array<String>) {
val action = args[0]
when (action) {
"aes" -> aes(args)
"ip" -> ip()
}
}
fun ip() {
val ip = Utils.getActiveIPv4Address()
println(ip)
}
fun aes(args: Array<String>) {
val data = args[1]
val encrypt = Aes.encrypt(data)
println(encrypt)
}
如上代码,如果要获取ip,运行main函数时只需要输入一个参数ip即可。然后重新生成一个jar文件,覆盖到Apifox指定的目录中。
然后在登录请求中添加ip参数,如下:
如上图,{{ip}}
表示读取环境变量ip
,我们在自定义脚本中就可以为这个环境变量赋值,点击 前置脚本,添加如下脚本代码:
这里一定要注意,需要把 “变量替换” 放在 “自定义脚本” 下面,如下:
为什么要这样呢?因为我们的自定义脚本只是设置了一个环境变量,具体把环境变量取出来替换到请求参数中是由 “变量替换” 这个脚本完成的,如果 “变量替换” 在上面,则变量替换会先执行,但是这时ip这个环境变量还没设置有值,那么发送请求时ip的值将原样发送,即发送 “{{ip}}
”,所以要让自定义脚本运行,这样自定义脚本就会先给环境变量赋值了,这样再到变量替换执行时,就可以把真正的ip替换到{{ip}}
上了,执行结果如下:
对于变量,有三种类型:全局/环境/临时变量,具体参考官方文档:https://docs.apifox.com/5537409m0