API 鉴权三种常用做法:用生活例子讲明白
没带工牌:保安不让进 → 接口返回 401/403(未授权)带了工牌:保安看一眼就让进 → 接口正常返回数据(已授权)API 鉴权,就是服务器先确认「你是谁」,再决定能不能访问这个接口。
本文介绍接口自动化里最常见的三种鉴权写法,并结合本框架说明怎么用。
先搞懂:什么是鉴权?
想象你去公司上班:
- 没带工牌:保安不让进 → 接口返回 401/403(未授权)
- 带了工牌:保安看一眼就让进 → 接口正常返回数据(已授权)
API 鉴权,就是服务器先确认「你是谁」,再决定能不能访问这个接口。
行业里常见吗?三种方式分别是什么?
在 Postman、JMeter、HttpRunner、pytest 等工具/框架里,自动化鉴权几乎都绕不开下面这件事:
先拿到凭证(Token / Cookie / Session)→ 存起来 → 后面的请求带上它
| 做法 | 行业里叫什么 | 常见程度 |
|---|---|---|
| 方式一:框架自动注入 Token | 前置登录 + 统一鉴权头 / Auth Hook | ⭐⭐⭐ 很常见 |
方式二:用 ${变量} 手动引用 |
参数关联 / 变量替换 | ⭐⭐⭐ 很常见 |
| 方式三:pytest Fixture 前置登录 | setup / session fixture | ⭐⭐⭐ pytest 项目里很常见 |
补充说明:
- 真实项目里还有 API Key、OAuth2、Basic Auth、Cookie-Session 等,但自动化脚本里「登录拿 Token,再带给后续接口」仍是最主流的一类。
- 方式一和方式二经常同时存在:默认用方式一省事,特殊用例用方式二精细控制。
- 方式三适合纯 Python 用例;YAML 数据驱动的项目优先用方式一。
无论哪种方式,核心都是三步
① 登录(或调用鉴权接口)拿到 Token
↓
② 存到「公共仓库」( Context,同时写内存和 extract.yaml)
↓
③ 后续请求带上 Token(框架自动塞,或 YAML 里写 ${auth_token})
下面分别说明三种做法。
方式一:Token 自动注入⭐
一句话理解
登录用例把 Token 存进仓库;后面只要在 YAML 里写 need_auth: true,发请求时框架自动把 Token 放进请求头——像游乐园「进门刷一次票,每个项目不用再单独买票」。
原理
登录 YAML(extract)→ Context 保存 Token → 业务 YAML(need_auth: true)→ RequestHandler 自动注入请求头
本框架完整流程(一次 pytest 运行)
pytest 启动
↓
conftest:清空 config/extract.yaml(避免上次残留的 Token 干扰本次)← 注意:不是「下次运行接着用」
↓
扫描 data 下所有 *_test.yaml,按路径+文件名排序(如 01_login 会排在 02_xxx 前面)
↓
┌─ 登录用例 ─────────────────────────────────────────┐
│ POST /api/example/login │
│ extract: │
│ auth_token: "data.auth" ← 从响应里抠出 Token │
└───────────────────────────────────────────────────┘
↓
响应 code=0 时 → Context.set("auth_token", "abc123") (内存 + extract.yaml)
↓
┌─ 业务用例 ─────────────────────────────────────────┐
│ need_auth: true ← 告诉框架:这接口要带 Token │
│ POST /api/example/get_list │
└───────────────────────────────────────────────────┘
↓
RequestHandler.send():
token = Context.get("auth_token")
headers 里没有 auth 时 → 自动补上 auth 和 Authorization: Bearer xxx
(若 YAML 里已经写了 auth,则不会覆盖,方便测「无效 Token」)
YAML 示例
1. 登录并提取 Token
-
title: "登录成功"
base_url_key: "base_url"
request:
method: "POST"
url: "/api/example/login"
headers:
Content-Type: "application/x-www-form-urlencoded"
data:
phoneCode: "86"
phoneNo: "13800138000"
captcha: "1234"
extract:
auth_token: "data.auth"
validate:
- eq: ["status_code", 200]
- eq: ["json.code", 0]
2. 业务接口:只加 need_auth
-
title: "查询课程列表"
base_url_key: "base_url"
request:
method: "POST"
url: "/api/example/get_list"
need_auth: true
headers:
Content-Type: "application/x-www-form-urlencoded"
data:
currentPage: 1
perPage: 10
validate:
- eq: ["status_code", 200]
- eq: ["json.code", 0]
和代码的对应关系
| 步骤 | 文件 | 做什么 |
|---|---|---|
| 每次运行清空旧 Token | conftest.py |
Context.clear_extract_file() |
| 用例排序、变量替换、提取 | test_api.py |
replace_vars → send → extract_and_set |
| 自动注入 Token | request_handler.py |
need_auth 为 true 时 Context.get("auth_token") |
| 存/取变量 | common/utils.py |
Context.set / get / extract_and_set |
提取 Token 时有个细节:只有响应里 code == 0 才会写入 Context,登录失败不会误存旧 Token。
Context 是干什么的?
可以把它想成本次测试运行的公共储物柜:
| 方法 | 存哪 | 作用 |
|---|---|---|
Context.set(key, val) |
内存 + config/extract.yaml |
保存变量,方便调试时打开文件看 |
Context.get(key) |
优先内存 | 取变量给注入或替换用 |
Context.replace_vars(data) |
不存 | 把字符串里的 ${auth_token} 换成真实值 |
关于 extract.yaml 的常见误解:
- ✅ 同一次
pytest运行里,前面用例存的 Token,后面用例能用。 - ❌ 不是「下次跑测试不用登录」——每次启动 pytest 都会先清空该文件。
优点
- ✅ 业务用例只写
need_auth: true,不用每个接口重复写 Token - ✅ 全 YAML 配置,改接口不用改 Python
- ✅ 和方式二共用同一套 Context,随时可切换手动引用
缺点
- ❌ 必须有一份「登录」YAML,且排序上要先于依赖 Token 的用例
- ❌ 不同项目鉴权头名字不一样时,要在
RequestHandler里统一适配(本框架默认补auth和Authorization)
方式二:在 YAML 里用 ${变量名} 手动引用
一句话理解
Token 还是登录时 extract 进 Context,但不用 need_auth,而是在 headers / data / params 里自己写 ${auth_token}——像「不刷通用年卡,每个项目单独出示门票」。
和方式一的区别
| 方式一 | 方式二 | |
|---|---|---|
| 谁把 Token 放进请求 | 框架(need_auth: true) |
你自己在 YAML 里写 ${auth_token} |
| 典型场景 | 大部分正常业务接口 | 测无效 Token、Token 放 body、Cookie 等特殊格式 |
YAML 示例
登录(与方式一相同)
extract:
auth_token: "data.auth"
业务接口:手动引用
-
title: "查询课程列表"
base_url_key: "base_url"
request:
method: "POST"
url: "/api/example/get_list"
headers:
Content-Type: "application/x-www-form-urlencoded"
auth: "${auth_token}"
data:
currentPage: 1
perPage: 10
测无效 Token(方式二更合适)
headers:
auth: "invalid-token-12345"
执行流程
登录 extract → Context 有 auth_token
↓
发请求前:对 headers / data / params / json 做 replace_vars
↓
"${auth_token}" 变成真实字符串 → 发出请求
框架会对 headers、data、params、json 都做替换,不只 data。
优点
- ✅ 灵活:Token 可放 header、body、Cookie 任意字段
- ✅ 方便写负向用例(故意写错 Token)
- ✅ 不依赖
need_auth开关
缺点
- ❌ 每个要鉴权的用例都要手写
${auth_token},容易漏写 - ❌ 鉴权头字段名不统一时,维护量比方式一大
方式三:pytest Fixture 前置登录
一句话理解
不(或不仅)在 YAML 里写登录,而是在 conftest.py 里用 pytest 的 Fixture 在测试开始前先登录——像「进园区前保安统一发工牌,再开始参观」。
和方式一的区别
| 对比项 | 方式一(YAML 登录) | 方式三(Fixture 登录) |
|---|---|---|
| 登录写在哪 | YAML 用例 | conftest.py 的 Fixture |
| 登录何时执行 | 跑到登录那条 YAML 时 | Fixture 的 scope 决定(常见 session = 整场只登一次) |
| Token 存哪 | Context(同方式一) | Context(同方式一) |
| 后续怎么带 Token | need_auth 或 ${auth_token} |
一样,本框架的 RequestHandler 不用改 |
| 更适合 | YAML 数据驱动 | 纯 pytest 函数用例 |
说明:方式三不是和方式一对立的另一套注入机制,只是「登录动作」从 YAML 挪到了 Fixture;带 Token 仍可用 need_auth 或方式二。
示例代码
conftest.py:会话级自动登录
import pytest
from common.request_handler import RequestHandler
from common.utils import Context
@pytest.fixture(scope="session", autouse=True)
def auto_login():
resp = RequestHandler.send(
method="POST",
url="https://api.example.com/api/account/login",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={"phoneCode": "86", "phoneNo": "13800138000", "captcha": "1234"},
)
if resp and resp.json().get("code") == 0:
Context.set("auth_token", resp.json()["data"]["auth"])
yield
测试用例:照常 need_auth
def test_query_course():
resp = RequestHandler.send(
method="POST",
url="https://api.example.com/api/v1/course/get_list",
need_auth=True,
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={"currentPage": 1, "perPage": 10},
)
assert resp.json()["code"] == 0
Fixture 两个常用参数
scope:登录执行几次
| 值 | 含义 |
|---|---|
function |
每个测试函数前各登一次 |
module |
每个 .py 文件登一次 |
session |
整场 pytest 只登一次(最常用) |
autouse:要不要每个测试都自动跑
| 值 | 含义 |
|---|---|
False(默认) |
只有测试函数参数里写了该 fixture 名才执行 |
True |
所有测试自动先执行该 fixture |
优点
- ✅ pytest 官方惯用写法,生命周期清晰(登录 / 建库 / 清数据都能放 fixture)
- ✅ 登录逻辑集中在 Python,适合复杂鉴权(加密、多步 OAuth)
缺点
- ❌ 要多写 Python,和「全 YAML 驱动」风格不太搭
- ❌
autouse=True+session时,不需要登录的用例也会先登一次(可用autouse=False按需引用避免)
附录:装饰器方式(了解即可,pytest 项目一般不首选)
有些教程会用 @login_required 这种装饰器在每个测试函数执行前调登录。它和 Fixture 的区别:
| 装饰器 | pytest Fixture | |
|---|---|---|
| 本质 | 包一层新函数 | 独立的前置/后置钩子 |
| 全局自动执行 | 要自己写 autouse 或逐个加装饰器 |
autouse=True 原生支持 |
| 在 pytest 里 | 能用,但不如 Fixture 直观 | 推荐 |
常见误区:
@pytest.mark.login_required # ❌ 只是打标签,不会自动登录
@login_required # ✅ 装饰器写法才会包一层 wrapper
def test_get_user():
pass
接口自动化里更常见的是方式一 / 二 / 三;装饰器多见于 Web 接口开发(如 Flask),测试项目里知道原理即可。
三种方式对比(一张表看完)
| 方式 | 怎么触发登录 | Token 放哪 | 怎么带给后续接口 | 最适合 |
|---|---|---|---|---|
| 一:自动注入 | YAML 登录用例 | Context | need_auth: true |
⭐ YAML 驱动 |
| 二:变量引用 | YAML 登录用例 | Context | ${auth_token} 写在 YAML |
特殊鉴权、负向用例 |
| 三:Fixture | conftest Fixture | Context | 同左两种均可 | 纯 pytest 代码用例 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)