接口自动化:接口自动化中的公共鉴权设计

接口自动化做久了之后,你会发现最让人烦躁的失败,通常不是业务断言失败,而是鉴权失败。

典型症状是:

  • token 过期导致一批 case 连续挂掉
  • 测试环境和预发环境登录入口不一致
  • 管理员和普通用户身份串用
  • 签名算法改动后,几十条接口同时失效
  • Jenkins 并发任务抢同一个共享账号,导致 session 混乱

如果公共鉴权没设计好,整套自动化看起来“覆盖很多”,实际每天都在被低价值失败拖垮。

一、为什么鉴权必须从 case 里剥离出来

这类坑在早期非常常见:在每个业务模块里自己处理登录和 header。

短期看很快,长期会出现三类问题:

  • 登录逻辑散落各处,改一次要全局搜代码
  • 多角色接口很容易串上下文
  • 401、403、签名错误这些失败很难统一归类

所以后来会把鉴权设计成框架的基础设施,和日志、配置、报告一样独立治理。

二、真实项目里会遇到哪些鉴权类型

不是所有系统都只是 Bearer Token。在实际项目里,常见模式包括:

  • 用户名密码换取 token
  • cookie / session 登录
  • JWT + refresh token
  • 时间戳 + nonce + HMAC 签名
  • 租户 ID + 用户 token 双重身份
  • 白名单 IP + token 混合控制

这意味着公共鉴权不能写死成一个 get_token()。它更像一层可插拔 provider。

三、会用哪些工具和手段

如果还是 Python 执行层,通常会用:

  • requests.Session:处理 cookie / session 保持
  • PyJWT 或本地解析逻辑:判断 JWT 过期时间
  • redis-py:做多任务共享凭证缓存
  • hashlib / hmac:处理签名型接口
  • threading.Lock 或分布式锁:控制并发刷新

为什么需要 Redis:

  • Jenkins 多任务并行时,单进程内存缓存不够用
  • 巡检、回归、专项任务都可能共用同一套凭证体系
  • 凭证共享以后,登录接口本身的压力会小很多

四、怎么拆鉴权模块

更常见的拆法是下面几层:

1
2
3
4
5
6
7
auth/
├── manager.py # 鉴权统一入口
├── providers/ # 各类登录实现
├── signers/ # 签名算法
├── cache/ # 凭证缓存
├── context.py # 角色与环境上下文
└── refresher.py # 失效刷新与重试

职责划分是:

  • provider 负责真正登录
  • cache 负责存 token / cookie / session
  • context 负责区分环境、角色、租户
  • refresher 负责未授权后的恢复动作
  • manager 对请求层暴露统一能力

这样后面认证方式变化时,不会把业务 case 一起拖着改。

五、一个更像真实项目的实现方式

以“管理后台 + 普通用户中心”并存的系统为例,会准备两组 provider:

  • AdminTokenProvider
  • UserTokenProvider

然后在配置中定义:

1
2
3
4
5
6
7
8
9
auth_profiles:
admin:
provider: admin_token
env: pre
username: autotest_admin
buyer:
provider: user_token
env: pre
username: autotest_buyer

业务 case 不直接写登录逻辑,只声明自己要哪种身份:

1
2
headers = auth_manager.inject("buyer", env="pre")
resp = client.post("/api/v1/orders", headers=headers, json=payload)

后面如果 buyer 这类身份从 token 换成 cookie session,业务 case 不需要跟着改。

六、失效恢复怎么做才不会制造噪声

很多自动化框架遇到 401 的处理方式非常粗暴:直接 fail。

不适合这么做。更合理的链路通常是:

  1. 第一次请求返回 401 或特定业务码
  2. 鉴权模块检查当前凭证是否接近过期
  3. 如果符合刷新条件,重新登录或刷新 token
  4. 原请求自动重放一次
  5. 若仍失败,将结果归类为“鉴权失败”而不是“业务失败”

这一步会直接影响告警质量。否则一次 token 过期,很可能触发几十条误报。

七、并发和多角色切换是最常见的隐藏问题

真正跑上 Jenkins 或平台后,两个问题最容易暴露:

1. 刷新风暴

同一时刻多个 worker 发现 token 过期,然后一起重新登录,导致认证服务压力升高,甚至把自己打挂。

解决方式通常是:

  • 先查缓存中的过期时间
  • 只有一个 worker 获得刷新锁
  • 其余 worker 等待新 token 写回缓存

2. 角色污染

一个 suite 里管理员和普通用户混用,如果上下文存放不规范,很容易把管理员 token 带到普通接口里。

更稳妥的做法是:

  • 凭证缓存 key 强制包含 env + role + tenant
  • 请求日志中打印当前身份标签
  • 断言失败时把身份上下文一起输出到结果 JSON

这样出了问题,定位会快很多。

八、签名类接口怎么做统一封装

很多内部系统不是纯 token,而是:

  • 当前时间戳
  • 请求体摘要
  • 固定密钥
  • nonce
  • 最后生成签名 header

这种场景如果放在业务 case 里手算,很快会失控。会把签名封装到 signer 中,由请求发送前统一注入。

比如:

1
request = signer.sign(request, secret=secret_key)

业务层只知道“这个接口需要 signed_auth”,不关心签名细节。

九、实现效果怎么判断

公共鉴权设计做完之后,会看这些结果:

  • 登录接口抖动时,是否只影响少量任务,而不是拖垮整批 case
  • 多角色场景下,是否还能保持上下文清晰
  • 报告中能否把 401 / 403 / 签名错误单独分类
  • 更换认证方式时,是否只修改 provider 层
  • Jenkins 并发执行时,是否不再频繁出现凭证抢占问题

如果这些都没有改善,说明所谓公共鉴权很可能只是“把登录函数集中放了一个文件”。

十、结语

接口自动化中的公共鉴权,本质上是一个“身份基础设施”问题。它解决的不是怎么登录一次,而是怎么让多角色、多环境、多任务执行下的身份上下文长期稳定、可恢复、可归类。真正成熟的自动化框架,鉴权从来不是附属函数,而是核心底座。

本章延伸阅读