先给一个判断方向:如果调用方是你自己的后端服务,凭证代表的是“这个应用”,用 API Key 通常就够;如果凭证要代表“某个用户”去访问 AI 资源,或者用户需要看到、管理、撤销这份授权,就应该走 OAuth。混合做法也很常见——面向用户的入口走 OAuth,内部服务之间继续用 API Key。
判断依据不是“哪个更安全”,而是“凭证代表谁”。代表应用、只在服务端之间传递、生命周期由自己掌控,选 API Key,并把密钥放进环境变量或密钥管理服务,禁止下发到前端。代表用户、需要用户同意与撤销、需要按人区分配额和权限,选 OAuth 的授权码流程,服务端保存 refresh token,前端只拿短期凭证。两者都不解决传输加密和日志泄露问题,需要额外处理。
先确认当前场景和目标
在动代码之前,先把下面几个问题答清楚,答案基本决定了选型,而不是反过来先选协议再找场景。
- 调用方是谁:浏览器、移动端、你自己的后端服务,还是第三方开发者接入你的平台。浏览器和移动端属于不可信环境,长期凭证不应落在其中。
- 凭证代表谁:代表一个应用(机器身份),还是一个具体用户(用户身份)。代表用户就要能回答“这个用户授权了什么、能撤销吗”。
- 是否需要用户同意:用户是否要看到授权页面、是否要按范围授予权限。需要就落在 OAuth 的授权码流程里。
- 生命周期与轮换:密钥多久换一次、泄露后多久能失效、能否按环境隔离。API Key 通常需要自己设计轮换机制,OAuth 的 access token 天生短期。
- 权限粒度:是否要区分“只能调用某几个模型”“只能读不能写”“按用户限额”。粗粒度用 Key 加白名单可以,细粒度按人区分建议走 OAuth 的 scope。
如果以上问题里出现“用户要撤销”“要按人计费”“要第三方接入”,基本可以确定需要 OAuth。如果只是自家后端调自家 AI 网关,先上 API Key,把精力放在密钥管理上更划算。
按这个顺序处理
建议按“先定身份 → 再定存放 → 再定校验 → 最后补轮换”的顺序推进,避免一开始就纠结协议细节。
第一步:写清凭证的身份和边界
用一句话描述凭证:谁持有、能调哪些接口、有效期多长、泄露后如何作废。这句话写不出来,说明场景还没定,先别写代码。
第二步:API Key 的落地骨架
密钥只放在服务端环境变量或密钥管理服务里,代码中不出现明文。下面是通用骨架,变量名按你的项目替换:
# .env(不要提交到仓库)
AI_API_KEY=replace-with-your-key
AI_BASE_URL=https://your-ai-gateway.example.com
# 服务端发起请求时注入请求头
Authorization: Bearer ${AI_API_KEY}
# 部分平台用自定义头,按实际网关约定替换头名
X-Api-Key: ${AI_API_KEY}
前端代码、构建产物、移动端安装包中都不应出现这把 Key。如果前端需要调用 AI 能力,让前端调你自己的后端,由后端持有 Key 再转发。

第三步:OAuth 的落地骨架
面向用户的场景走授权码流程,服务端换 token,浏览器只拿短期 access token 或干脆只拿会话 Cookie:
# 1) 引导用户授权(scope 按最小权限填写)
GET /authorize?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://your-app.example.com/callback
&scope=ai.read
&state=RANDOM_STATE
# 2) 回调后用 code 换 token(服务端执行)
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=RETURNED_CODE
&redirect_uri=https://your-app.example.com/callback
&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
返回的 refresh token 存服务端,access token 只在内存或短期存储中使用。state 参数要校验,回调地址要白名单,这两点常被漏掉。
第四步:混合场景的分层
常见做法是外层 OAuth 识别用户,内层用一个独立的后端 API Key 去调 AI 服务。这样用户侧可以撤销授权,同时 AI 服务的凭证不会因为用户退出而失效。两层凭证放在不同组件里,不要串用。
第五步:补上轮换和撤销
API Key 至少准备“新旧双活”的过渡方案:先加新 Key,观察调用方切换完成,再停旧 Key。OAuth 侧确认 refresh token 的失效逻辑和解绑入口真的可用。
改完后怎么验证
验证要看信号,不要只看“能调通”。以下动作建议逐条执行:
- 不带任何凭证请求受保护接口,预期返回 401,而不是 200 或 500。
- 用一把已作废或过期的 Key 请求,预期返回 401,并确认错误信息里不回显 Key 内容。
- 用 scope 不足的 token 请求超出范围的接口,预期返回 403,且提示缺少哪个范围。
- 在 OAuth 流程中篡改
state或回调地址,预期被拒绝,不进入发码环节。 - 在服务端日志、网关日志、异常上报中搜索密钥片段,预期查不到明文;如果查得到,先处理日志脱敏再继续。
- 对前端构建产物做一次关键字扫描,确认没有出现密钥字符串。
- 走一遍用户撤销授权的流程,预期后续请求被拒绝,且不会静默降级成共享 Key。
预期信号是:越权和缺失凭证都表现为明确的 401/403,而不是业务错误码或超时。出现后者通常说明鉴权层放在了业务逻辑之后,需要前移。
容易忽略的边界
- OAuth 不等于加密:OAuth 解决的是授权委托和凭证生命周期,传输仍然要靠 HTTPS,token 泄露后的风险主要靠短期有效期控制。
- 浏览器和移动端不适合长期凭证:把 API Key 打进前端包里,等同于公开。此类场景建议改为后端持有凭证,或走带 PKCE 的授权码流程;具体支持情况需要结合你使用的平台确认。
- 反向代理和网关会记录请求头:鉴权头经过多层转发时,注意哪些中间层会落日志。必要时在网关层剥离或脱敏后再转发。
- 多环境隔离:开发、测试、线上使用不同 Key 和不同 OAuth 客户端,避免测试流量打到线上配额。
- 不适用条件:如果团队没有密钥管理能力,也没有人力维护 token 刷新和轮换,先收敛到单一 API Key 加固定出口,比半套 OAuth 更稳。
- 回退建议:切换过程中保留旧鉴权方式一段时间,用开关控制,确认新链路稳定后再关闭;出问题时能快速切回,而不用回滚整个版本。