企业接口自动化中的登录鉴权设计:SSO、Token 缓存与多账号切换实战

前言

接口自动化项目里,登录鉴权经常是第一个看起来“应该不难”,但实际上最容易翻车的部分。

原因很简单:

  • 真实项目的登录链路往往不止一步

  • 一个系统可能不止一种 token

  • 不同角色、不同租户、不同系统之间的鉴权方式都可能不同

  • token 失效、缓存污染、账号串用的问题非常常见

如果把这些逻辑直接写在每条用例里,项目很快就会变得不可维护。

所以在我的这个项目里,鉴权被单独抽成了一层,核心代码集中在 common/auth_manager.pyclient/api_client.pyfixtures/api_fixtures.py

这一篇我想讲的不是“如何登录”,而是为什么一定要把登录鉴权从用例中剥离出来,以及这样做到底解决了哪些真实问题。

一、先看真实的鉴权链路

这个项目不是一个简单的“登录后拿 Bearer Token”模式,而是有比较典型的企业级链路:

1)SSO 登录

先通过 SSO 登录接口拿到重定向地址,再从 URL 中解析授权码 code,最后用 code 去交换 ssoToken

2)业务系统B 登录

在拿到 ssoToken 之后,再结合 tenant_idclient_id 等信息,进一步换取 系统B 即业务侧的 token。

3)业务系统访问

不同业务系统会在请求头中携带不同的默认头,比如:

  • system-code

  • tenant-id

  • client-id

  • ssoToken

  • Authorization

也就是说,在这个项目里,鉴权不是一个动作,而是一条链路。

二、为什么鉴权必须单独抽层

很多人做接口自动化时会忽略一个问题:

登录其实是最容易和业务测试耦合的地方。

如果你把登录流程直接写进测试用例,后果通常有这些:

1)用例可读性迅速下降

原本只是想测库存列表,结果用例前面先写了几十行登录逻辑。

2)登录逻辑很难统一修改

例如登录接口路径改了、返回结构改了、授权码获取逻辑改了,你得改所有用例。

3)token 复用能力差

大量用例执行时,如果每条都重新登录,速度慢不说,还容易引发风控或不稳定。

4)角色切换不清晰

普通用户、操作员、管理员、不同租户的 token 如果都写在用例里,后期很难区分谁在访问什么。

所以更合理的做法是:把登录归到基础设施层,而不是业务验证层。

三、AuthManager 的定位

common/auth_manager.py 里的 AuthManager,承担的是“统一拿 token、统一缓存 token、统一失效 token”的职责。

它用了一个比较实用的方式:

  • 单例模式

  • 内存缓存

  • 按账号、系统、租户组合 key

核心想法很简单:

同一个账号、同一个系统、同一个租户,如果 token 还有效,就不要重复登录。

这样设计有几个明显好处:

1)避免重复登录

session 级测试执行时,一个 token 可以被大量用例复用。

2)减少登录接口压力

接口自动化本身不是压力测试,没必要因为测试框架设计不合理而频繁打登录接口。

3)让 token 生命周期更可控

缓存和失效都集中处理,出问题时更好定位。

4)便于扩展多账号体系

以后如果再增加测试账号,只要再补一组 key 规则就行,不需要改一堆用例。

四、SSO 登录为什么是“先 code 再 token”

项目中的 SSOClient 做的是比较典型的企业统一登录流程。

它不是直接调用一个接口拿 token,而是分成两步:

第一步:登录

通过用户名、密码、登录来源、系统编码等信息,拿到一个重定向 URL。

第二步:解析授权码并换 token

从 URL 中解析 code,再用 code 调换 ssoToken

这一点的意义在于:

  • 登录流程和 token 颁发流程被拆开了

  • 登录失败和 token 交换失败可以更清晰地定位

  • 兼容企业里常见的统一认证中心模式

对应代码就在 client/api_client.pySSOClient.login()SSOClient.exchange_token()

五、为什么要把登录逻辑封装成方法而不是脚本

如果只是脚本,通常会出现这样的问题:

  • 代码可读性差

  • 登录参数散落

  • 重定向解析到处复制

  • 错误处理不统一

而封装成方法之后,测试框架会得到几个好处:

1)登录流程可复用

不管是默认用户还是操作员用户,都能复用同一套登录逻辑。

2)异常处理统一

如果 code 提取失败,或者 token 没取到,可以统一抛出明确错误。

3)便于替换实现

如果未来登录方式变了,比如由页面跳转改成接口直登,你只改一层封装,不需要动所有用例。

4)更符合测试基础设施思维

测试用例的重点应该是“业务验证”,不是“怎么登录”。

六、业务系统B Token 的获取为什么要单独处理

很多企业项目里,SSO Token 并不能直接访问所有业务系统。

这个项目里就有一个很典型的链路:

  1. 先拿 SSO Token

  2. 再基于 SSO Token 和租户信息去获取 业务系统B Token

  3. 最后带着 业务系统B Token 调业务接口

这个设计背后体现的是企业系统常见的权限边界:

  • 统一认证中心负责身份确认

  • 业务系统负责具体授权

  • 租户信息负责隔离业务数据

所以 AuthManager._do_业务系统B_login() 的存在不是多余,而是把“身份”和“业务访问”这两层关系明确区分开。

如果不这样做,后面很多业务场景都会变得很难解释:

  • 为什么同一个人能访问某些系统,不能访问另一些系统

  • 为什么同一个 SSO Token 在不同租户下看到的数据不一样

  • 为什么请求头里必须带 tenant-id

把这些问题抽出来,整个项目就清楚很多。

七、fixture 为什么是鉴权落地的最佳位置

这个项目在 fixtures/api_fixtures.py 里把鉴权做成了 pytest fixture。

这是非常合理的,因为 pytest 的 fixture 天生适合做“前置准备”。

例如:

@pytest.fixture(scope="session") def sso_token(): return AuthManager().get_sso_token(...)

这意味着:

  • 一个 session 里 token 可以复用

  • 用例里不需要重复写登录步骤

  • 登录失败会在最早的地方暴露

又比如:

@pytest.fixture(scope="session") def 业务系统A_client(sso_token): client = 业务系统AClient() client.session.headers.update({"ssoToken": sso_token}) return client

这一步更重要,因为它把“已登录的业务客户端”直接注入给测试用例。

用例拿到的是一个已经准备好的 client,而不是自己重新拼 header。

八、多账号、多角色为什么要分开设计

这个项目里其实已经预留了两类账号:

  • 默认用户

  • 操作员用户

这类设计在企业系统里非常必要,因为不同角色看到的数据和权限往往不一样。

例如:

  • 普通用户只能查自己权限范围内的数据

  • 操作员可能可以看到更多库存或管理数据

  • 不同租户的数据互相隔离

如果这些角色不分开,测试结论就会非常模糊。

所以你在 fixture 里单独做了 operator_sso_token 和 业务系统A_operator_client,这是非常符合实际业务的做法。

它的好处是:

  1. 角色边界清晰

  2. fixture 命名清晰

  3. 用例意图清晰

  4. 后期扩展第三类账号也容易

九、鉴权里最容易踩的坑

1)缓存了 token,但没有考虑失效

缓存本身没问题,问题是缓存后忘了做失效处理。

如果 token 过期了,但你还一直拿缓存值用,就会出现一串无意义的失败。

所以 invalidate() 的存在是必要的。

2)登录态串号

如果多个系统共用同一个 session 或同一套 header,而默认头没有隔离好,就可能把 A 系统的 token 带到 B 系统里。

这个问题在多系统项目里非常常见。

3)重复登录导致不稳定

很多接口自动化一旦变慢,常常不是业务接口慢,而是登录流程重复执行太多。

4)测试数据和鉴权账号不匹配

例如账号权限不足,却要访问管理员数据,最后你会误以为接口失败,实际是账号不对。

所以账号、租户、系统、数据类型要一起设计。

十、为什么这个项目适合把鉴权做成“基础设施”

因为你的项目不是一个单系统小脚本,而是一个多系统、多角色、多租户的接口自动化框架。

在这种项目里,鉴权不是附属功能,而是底层能力。

如果底层没做好,后面所有业务场景都会被拖累。

反过来说,只要鉴权层稳定了:

  • 用例会变短

  • 服务层会更清晰

  • 业务场景会更自然

  • 报错会更好定位

这就是把鉴权单独做成基础设施的价值。

十一、小结

登录鉴权在接口自动化里,真正难的不是“怎么拿到 token”,而是:

  1. 怎么把登录流程从用例中剥离

  2. 怎么复用 token 而不串号

  3. 怎么支持多账号、多系统、多租户

  4. 怎么让登录失败尽早暴露

这个项目已经把这套思路做出来了,而且是比较符合企业落地的方式。

补充:解释下单例模式

一个类在整个进程里只创建一个实例,后面再创建时拿到的还是同一个对象。

在这个项目里,common/auth_manager.py 和 common/config_manager.py 都用了这种思路。

它解决什么问题

  • 避免重复初始化
  • 统一管理共享状态
  • 方便做缓存,比如 token、配置、连接信息

在项目里的体现

  • AuthManager 负责登录鉴权和 token 缓存
  • ConfigManager 负责只加载一次配置文件
  • 它们都通过 new 控制实例生成:
  • 第一次创建时,生成对象
  • 之后再 AuthManager() / ConfigManager(),返回同一个对象

你可以把它理解成:

a = AuthManager()

b = AuthManager()

assert a is b # True

为什么鉴权里常用单例

  • token 通常希望全局复用,别每个用例都重新登录
  • 配置文件也不想反复读取
  • 鉴权流程比较重,单例能减少重复开销

但要注意

  • 单例模式只保证“同一个进程里”只有一个实例
  • 如果是多进程跑测试,比如 pytest-xdist,每个进程还是会各自有一个单例
  • 单例适合“全局共享、状态稳定”的对象,不适合每个用例都应该隔离的对象

一句话总结

  • 单例 = 保证一个类只有一个实例
  • 在这个项目里,它主要用来做 配置统一加载 和 鉴权 token 统一缓存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tizzy JJ

赞助一次「咖啡续命」费用

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值