You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gatling中Auth Token传递问题:如何复用登录生成的Token

解决Gatling中登录Token传递到请求头的问题

核心问题分析

你当前代码的两个关键问题:

  1. 会话隔离:scn1和scn2是独立场景,并发注入时两个场景的虚拟用户会话完全隔离,scn2的用户根本拿不到scn1用户生成的tokenId
  2. 静态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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 21:01:04