1. “登录了,为什么不认识我?”(问题的开端)

OAuth2、JWT、会话认证… 认证方式实在太多了,大多数情况下已经足够。我也曾如此。

  • 当在自己写的邮件客户端或 ChatGPT 的 MyGPT 中加入 OAuth2 时,感觉“这才是真正的用户体验”。
  • 单纯的 Django 服务器的 Web 应用,会话认证 是最佳选择!
  • 前后端分离的项目中,JWT 最为简洁。

然而,这些组合在某个时刻会一起崩塌。

关键点是 异步任务(Celery)。用户点击按钮时,后端并不是直接处理,而是把任务交给远程的 AI 计算服务器或 worker。此时 worker 会说:

“我收到了请求,但这是谁的请求?没有 request.user”。

机器人 worker 手持 api key 递交信件的图片

2. 问题在于 “后端 ↔ 后端” + “异步 worker(Celery)”

我决定引入 API Key 的根本原因是 后端服务器之间的通信,而 Celery worker 介入其中的结构。

用户点击按钮后,请求并不会直接到达 AI 计算服务器,而是:

  1. 用户发送 Web 请求
  2. 后端把“任务”放入队列
  3. Celery worker 消费队列,并 向某个后端/计算服务器发起异步请求

在这种情况下,最痛的点是:

  • worker 没有 request.user
  • 没有会话(因为不是浏览器)
  • JWT 也很尴尬(谁来管理、在哪里、如何传递 token)
  • OAuth2 更是需要用户交互,根本不可用

当 JWT 和会话失效时,唯一剩下的问题被压缩为:

“计算服务器如何知道是哪个客户(租户/用户)在执行这项任务?”

3. 在 worker 世界里,先要“识别”再谈“认证”

在 Web 请求中,认证 = 登录登录 = 用户,自然衔接。 但 worker 不是人,而是 自行借用 CPU 运行的应用程序,因此在它们之间,先要“识别”。认证可以通过服务器间的 HMAC 或 secretKey 解决。

  • 任务必须使用 A 客户 的数据执行
  • 结果必须保存到 A 客户 的资源中
  • 计费/权限/配额也要基于 A 客户 扣除

如果硬要用 JWT 或会话去套,这会导致 token 的颁发、存储、传递、过期、刷新设计膨胀,而且会产生一种强烈的违和感:

“即使是我的服务器的客户,但用户并未在浏览器中操作,后端去为其获取 JWT?”

这种想法太不自然,我立刻放弃了。

4. 解决方案:API Key 在此环节既简单又强大

于是我采用了 API Key,它一次性解决了所有问题。

  • worker 发起内部请求时,只需要 一个 Header 即可完成 认证+识别
  • 服务器凭此 key 能立即 映射到具体的用户/客户
  • Key 的回收/更换(轮转)也非常明确

示例请求如下:

POST /v1/ai/jobs
Authorization: Api-Key <KEY>
Content-Type: application/json

{ "job_id": "...", "payload": {...} }

即使 worker 没有 request.user,只要把上面的请求发出去,接收方后端即可通过文中说明的方式利用 API Key 确认用户身份。

5. 关键改进:把 API Key 与 USER 绑定后运维变得轻松

我特别满意的是这点:

常见的 rest_framework_api_key 库提供了基本的 Key 功能,但我的场景需要 “Key ↔ 用户(客户)绑定”。于是我继承 AbstractAPIKey,实现 CustomAPIKey,并通过 FK 关联到 AUTH_USER 模型

效果令人惊喜。

from django.conf import settings
from rest_framework_api_key.models import AbstractAPIKey
from django.db import models

class CustomAPIKey(AbstractAPIKey):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="api_keys"
    )
    is_test = models.BooleanField(default=False)  # 区分阶段/测试 Key

这样一来,除了普通的 认证,还能打开一系列 运维功能

6. 为每个用户自动发放 Key 带来的运维收益

在用户注册时自动为其创建一个 Key,运营上获得了以下便利:

1) 便于管理有效性

  • 查询/禁用/删除某用户的 Key 非常简单
  • 用户注销时,关联的 Key 会随之级联删除

2) Key 轮转更容易

  • 遇到“Key 泄漏”时,直接生成新 Key 并废除旧 Key,流程清晰
  • 支持多 Key,可实现 无缝切换(新 Key 部署后再废除旧 Key)

3) 计费/配额/权限可以以用户为单位

  • 不再是基于单个 Key,而是 基于用户 进行计费和限制
  • 不需要每次都去推断 “这个 API Key 属于谁”

4) 单个用户可拥有多个 Key

is_test 之类的标记在这里发挥作用。因为 API Key 通过 FK 关联,而不是 OneToOne,单个用户可以拥有多把钥匙用于不同场景。

  • 同一用户可以同时拥有 测试环境 Key生产环境 Key
  • 开发/运维流程可以清晰分离
  • 在日志/监控中也能轻松区分 “测试流量” 与 “真实流量”

7. 认证方式不是优劣之分,而是“情境武器选择”

总结我目前的最佳组合如下:

  • OAuth2:适用于外部服务/客户端集成、需要用户授权的场景
  • 会话认证:单体 Django Web 应用,开发速度快、实现最简
  • JWT:前后端分离、移动端/SPA 等多客户端场景的平衡方案
  • API Key:后端‑后端、自动化/worker/批处理等 非用户请求 场景的首选

尤其在 Celery worker 介入时,试图用 登录认证 统一整个体系只会增加复杂度。此时 API Key 成为最干净的出路。

8. 结语

人(浏览器/App)自然使用会话/JWT/OAuth2 来处理。 但 worker 不是人,而是进程,必须能够 识别出它代表的是哪个用户的工作

我转向 API Key 并不是因为宏大的安全论证,而是因为它在那个环节最简洁地解决了问题。把 Key 与 USER 绑定后,Key 管理不再是工具,而是 运维杠杆

大家在项目中也经常使用 API‑Key 吗?我分享的方式只是 API‑Key 便利性的一个切面,希望能为阅读此文的你带来一点灵感。


相关链接