给接口加把锁:Bearer Token 鉴权(附冒烟验收)

「从零到 AI 应用工程师」专栏 · 第 8 篇

阶段 1 · chat-api 收官补篇


上一篇用 Docker Compose 把整栈拉起来了。你会发现:几乎每条 curl 都带着这一行------

bash 复制代码
-H "Authorization: Bearer 你的令牌"

却很少有人单独讲清楚:这把锁是怎么挂上的?漏带会发生什么?如何用脚本证明「鉴权真的生效」?

chat-api 阶段再补这一刀:把鉴权做成可复用的依赖,再用一份冒烟脚本当交付验收。做完,阶段 1 才算真正能交出去。


一、今天要解决什么

目标 :未带 Token / Token 错误 → 401;正确 Token → 正常进业务。

最终效果(示意):

bash 复制代码
# 没锁 → 进不去
curl -i -X POST http://127.0.0.1:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"user_id":"u1","session_id":"s1","message":"你好"}'
# HTTP/1.1 401 ...

# 有锁 → 正常
curl -i -X POST http://127.0.0.1:8000/chat \
  -H "Authorization: Bearer 你的令牌" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"u1","session_id":"s1","message":"你好"}'
# HTTP/1.1 200 ...

探活接口(/ping/healthz)可以不鉴权------运维探活和业务读写要分开。


二、Bearer 到底是什么

HTTP 常见做法:在请求头里带:

http 复制代码
Authorization: Bearer <token字符串>
含义
Bearer 「持票人」模式:谁持有这串令牌,谁就被当成已授权
Token 一串密钥;本阶段用配置里的固定字符串即可(演示/内网够用)
依赖 Depends FastAPI 在进路由函数之前跑校验;失败直接 401,业务代码零侵入

本阶段不做完整 OAuth2 / JWT 签发体系。先把「接口默认不裸奔」立住;以后再换成验签、过期、多级 Key。

安全底线(写进习惯):

  • Token 放环境变量 / .env不进 Git、不写进镜像层
  • 对外文档用 你的令牌 / sk-xxx 占位,不贴真实值;
  • 日志里不要打印完整 Authorization。

三、最小实现:verify_token 依赖

python 复制代码
# core/auth.py(示意)
from fastapi import HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

# auto_error=False:不要框架默认文案,自己抛,方便统一成「说人话」的 JSON
security = HTTPBearer(auto_error=False)

# 从配置读取,例如环境变量 API_TOKEN
EXPECTED_TOKEN = "你的令牌"  # 实际:os.getenv("API_TOKEN")


async def verify_token(request: Request):
    credentials: HTTPAuthorizationCredentials | None = await security(request)
    if not credentials or credentials.credentials != EXPECTED_TOKEN:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效或未提供 Token",
            headers={"WWW-Authenticate": "Bearer"},
        )

挂到路由上------两种常见写法:

python 复制代码
# 写法 A:整个路由模块统一要鉴权
router = APIRouter(
    prefix="/chat",
    tags=["对话"],
    dependencies=[Depends(verify_token)],
)

# 写法 B:单个接口要鉴权
@router.post("/chat", dependencies=[Depends(verify_token)])
async def chat(...):
    ...

业务函数里不用 再写一遍 if token != ...。鉴权失败到不了你的 service 层,也就打不到大模型------既省钱,也少一次无效调用。

和「统一异常」篇衔接:若你已把 HTTPException 转成统一 JSON,401 也会变成:

json 复制代码
{
  "code": 401,
  "message": "无效或未提供 Token",
  "data": null,
  "request_id": "req_xxxxxxxxxxxx"
}

前端只认一种结构;监控也能按真实 HTTP 401 计数。


四、哪些要锁、哪些别锁

接口 建议 原因
POST /chat、历史查询等业务 要锁 直接花钱、读用户数据
GET /ping 可不锁 进程活着即可
GET /healthz 可不锁 K8s/Compose 探活;再加鉴权反而难运维
文档 /docs 开发开、生产关或加保护 避免接口清单裸奔

原则:探活宽松,业务收紧。


五、冒烟验收:用脚本证明「锁生效」

光靠手测容易漏。阶段 1 收官时,准备一份 smoke_test.sh(名字随意),至少覆盖:

  1. /ping/healthz 成功;
  2. 错误 Token → 401
  3. 正确 Token → /chat 成功;
  4. (可选)再问同一句,看缓存命中字段;
  5. (可选)拉一页历史。

伪代码节奏:

bash 复制代码
BASE_URL="${BASE_URL:-http://127.0.0.1:8000}"
API_TOKEN="${API_TOKEN:-你的令牌}"

# 1) 探活
curl -sf "$BASE_URL/healthz" >/dev/null || { echo "healthz FAIL"; exit 1; }

# 2) 鉴权必须失败
code=$(curl -s -o /dev/null -w "%{http_code}" \
  -X POST "$BASE_URL/chat" \
  -H "Authorization: Bearer wrong_token" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"u","session_id":"s","message":"ping"}')
[ "$code" = "401" ] || { echo "auth FAIL want 401 got $code"; exit 1; }

# 3) 鉴权通过
curl -sf -X POST "$BASE_URL/chat" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"u","session_id":"s","message":"冒烟你好"}' \
  | grep -q '"code": *200' || { echo "chat FAIL"; exit 1; }

echo "smoke PASS"

Compose 起来之后跑一遍:

bash 复制代码
docker compose up -d --build
./scripts/smoke_test.sh

全绿,你才有底气说:别人按 README 操作,十分钟能验证你的交付。


六、常见坑

  1. 只校验「有没有 Header」,不校验值。 → 任意 Bearer xxx 都能进。
  2. Token 写死在代码里并提交。 → 换成环境变量;仓库只留 .env.example
  3. /healthz 也鉴权了,却忘了给探针配 Header。 → 探活失败,编排系统反复杀容器。
  4. 401 仍返回 HTTP 200 + code:401 → 监控和网关会误判;状态码要真实。
  5. Swagger 里点「Authorize」忘了填,误以为接口挂了。 → 先确认 401 文案是鉴权,再查业务。
  6. 日志打印完整 Token。 → 最多打前后几位,或只打「已校验」。

七、阶段 1 真正收官

到这一篇,chat-api 骨架可以这样交卷:

能力
02 FastAPI + 大模型,打通 /chat
03 路由 / 业务 / 客户端分层
04 统一响应、统一异常、request_id
05 PostgreSQL 历史 + 分页
06 Redis 缓存重复问题(可降级)
07 Docker Compose 一键拉起整栈
08 Bearer 鉴权 + 冒烟脚本验收

底座齐了:能对话、能记账、能省钱、能一键起、默认不裸奔


八、带走这三条

  1. 鉴权用 Depends 前置拦截:失败不到业务、不打模型。
  2. 探活与业务权限分离/healthz 宽松,/chat 收紧。
  3. 冒烟脚本里必须有一条「错误 Token → 401」:证明锁是真的。

下一阶段进入专栏最硬核的一块:RAG------让大模型读懂你自己的资料,还不许瞎编。 第 9 篇先把概念和全貌讲清,再进入解析、分块、向量检索。

这是专栏第 8 篇,chat-api 阶段正式收官。两到三天一更,RAG 见。

相关推荐
u0103055271 小时前
昇腾Model Agent模型适配指南
人工智能
Neptune231 小时前
Agent死循环6小时:从“相信LLM判断”到“焊死硬件条件止
人工智能
用户548775431601 小时前
DeepSeek Harness 与 xAgent:两种 Agent Harness 架构路线怎么选
人工智能·deepseek
安逸sgr1 小时前
卷积神经网络 CNN 是什么?为什么适合处理图像?
人工智能·ai·大模型·agent·智能体
人工智能AI技术1 小时前
告别AI瞎编API!GitMCP实战:Web‑CAD开发准确率拉满
人工智能
用户73499134716531 小时前
为 DeepSeek V4 Flash 加上视觉:40M 参数连接器实战
人工智能
workflower1 小时前
智能无人机成低空经济核心赛道
运维·人工智能·机器学习·机器人·云计算·无人机
谁在黄金彼岸1 小时前
12 小时 50k Star:DeepSeek Harness(dsh)到底是什么,凭什么?
人工智能
沐风___2 小时前
Agent Skills 实测:一条命令给 AI 编程工具装上工程素养
人工智能