Gatling中Auth Token传递问题:如何复用登录生成的Token
解决Gatling中登录Token传递到请求头的问题
核心问题分析
你当前代码的两个关键问题:
- 会话隔离:
scn1和scn2是独立场景,并发注入时两个场景的虚拟用户会话完全隔离,scn2的用户根本拿不到scn1用户生成的tokenId - 静态Header错误:
common_headers是提前定义的静态Map,直接写"Bearer " + $tokenId语法完全错误——Gatling的会话变量只能在运行时通过表达式或会话函数获取,静态Map里无法引用
具体解决方案
1. 合并场景:让同一个用户完成登录+后续请求
每个虚拟用户必须先执行登录获取Token,再执行员工列表请求,所以要把两个场景的步骤合并成一个完整的用户流程:
val scn = scenario("管理员完整流程") // 登录步骤 .exec(http("管理员登录") .post("/api/user/login") // 已经通过httpProtocol设置了baseUrl,无需拼接全路径 .headers(login_headers) .formParam("username", admin) // 原代码写的admin123是错误的,用定义好的admin变量 .formParam("password", pwd) // 同理用pwd变量 .check(jsonPath("$.authToken").saveAs("tokenId"))) .exec { session => println(s"已获取Token: ${session("tokenId").as[String]}") session } // 员工列表请求步骤 .exec(http("获取所有员工列表") .post("/api/employee/lists/") // 动态设置请求头,用${tokenId}引用会话变量 .headers(Map( "Accept" -> "*/*", "Accept-Encoding" -> "gzip, deflate, br", "Accept-Language" -> "en-GB,en;q=0.9,en-US;q=0.8", "Authorization" -> "Bearer ${tokenId}", "Connection" -> "keep-alive" )) .body(RawFileBody("test/TestEnv4trial/employees_request.json")).asJson)
2. 复用Header的正确方式
如果想复用公共Header,可以选择以下两种方式:
方式一:用表达式定义动态Header
把common_headers定义为Expression[Map[String, String]]类型,运行时从会话中获取Token:
val common_headers: Expression[Map[String, String]] = session => { val token = session("tokenId").as[String] Map( "Accept" -> "*/*", "Accept-Encoding" -> "gzip, deflate, br", "Accept-Language" -> "en-GB,en;q=0.9,en-US;q=0.8", "Authorization" -> s"Bearer $token", "Connection" -> "keep-alive" ) }
然后在请求中使用:
.exec(http("获取所有员工列表") .post("/api/employee/lists/") .headers(common_headers) .body(RawFileBody("test/TestEnv4trial/employees_request.json")).asJson)
方式二:静态Header+动态添加Authorization
保留静态的公共Header(去掉Authorization),然后在请求中单独添加动态的Authorization头:
val common_headers = Map( "Accept" -> "*/*", "Accept-Encoding" -> "gzip, deflate, br", "Accept-Language" -> "en-GB,en;q=0.9,en-US;q=0.8", "Connection" -> "keep-alive" ) // 请求中添加动态Header .exec(http("获取所有员工列表") .post("/api/employee/lists/") .headers(common_headers) .header("Authorization", "Bearer ${tokenId}") // 用El表达式引用会话变量 .body(RawFileBody("test/TestEnv4trial/employees_request.json")).asJson)
3. 修正原代码的其他小错误
- 原代码中
formParam("username", admin123)和formParam("password", pass123)是错误的,应该用你定义好的admin和pwd变量 post({BaseUrl} + "/api/user/login")可以简化为post("/api/user/login"),因为httpProtocol已经设置了baseUrl- 不要并发注入独立的
scn1和scn2,否则scn2的用户没有登录Token,必然失败
完整修正后的代码示例
package apitest import scala.concurrent.duration.* import io.gatling.core.Predef.* import io.gatling.http.Predef.* import scala.language.postfixOps class TestEnv4trial extends Simulation { val e1: String = "https://testenv1-dev.net" val e2: String = "https://testenv2-dev.net" val baseUrl: String = e1 val pwd: String = "pass123" val admin: String = "admin123" val httpProtocol = http .baseUrl(baseUrl) .inferHtmlResources() val loginHeaders = Map( "Accept" -> "*/*", "Connection" -> "keep-alive", "Content-Type" -> "application/x-www-form-urlencoded;charset=UTF-8" ) val commonHeaders = Map( "Accept" -> "*/*", "Accept-Encoding" -> "gzip, deflate, br", "Accept-Language" -> "en-GB,en;q=0.9,en-US;q=0.8", "Connection" -> "keep-alive" ) val scn = scenario("管理员操作流程") .exec(http("管理员登录") .post("/api/user/login") .headers(loginHeaders) .formParam("username", admin) .formParam("password", pwd) .check(jsonPath("$.authToken").saveAs("tokenId"))) .exec { session => println(s"获取到的Token: ${session("tokenId").as[String]}") session } .exec(http("获取所有员工列表") .post("/api/employee/lists/") .headers(commonHeaders) .header("Authorization", "Bearer ${tokenId}") .body(RawFileBody("test/TestEnv4trial/employees_request.json")).asJson) setUp( scn.inject(atOnceUsers(1)) .protocols(httpProtocol) ) }
关键要点
- 会话隔离原则:Gatling每个虚拟用户都有独立的会话上下文,跨场景的用户不会共享变量,必须让同一个用户完成完整的操作链路
- 动态变量引用:会话变量必须用
${变量名}的El表达式,或通过会话函数在运行时获取,静态Map中无法直接引用 - 代码规范:变量名建议使用驼峰式(如
baseUrl、loginHeaders),符合Scala语言规范
内容的提问来源于stack exchange,提问作者Swapnil
相关产品推荐
相关产品推荐

