Agent 调用企业 API 还在用明文 Token?货拉拉这套 CLI/SSO 鉴权方案值得抄
Agent 调用企业 API 还在用明文 Token?货拉拉这套 CLI/SSO 鉴权方案值得抄
你的 Agent 还在裸奔调 API?这篇文章可能会改变你的做法。
2026 年 3 月底,飞书开源了飞书 CLI,一个多月拿了 1 万+ star。这个项目的意义不只是"把飞书功能搬到命令行"——它第一次系统性地展示了企业系统如何以「Agent 友好」的方式开放能力。文档、审批、日历、消息,全变成可组合的命令,大模型直接当函数调用。
但当你真想把企业内部的业务 CLI 接入 Claude Code、OpenClaw 这些 Agent 框架时,第一个撞上的墙不是接口不够用,而是——鉴权。
货拉拉技术团队最近公开了他们的解法:一套面向 Agent Skill 的 CLI/SSO 鉴权体系。核心卖点三个:Token 不落盘不暴露、多用户隔离、飞书一键授权无感知。我读完了他们的技术方案,觉得这套架构的设计思路值得每个在做 Agent 集成的人认真看看。
本文提纲
- 传统 API Key 为什么在 Agent 场景下翻车
- 四层架构:sso-cli、业务 CLI、sso-sdk、SSO 平台
- 安全存储:Keychain 主密钥 + 加密文件
- 多用户隔离:加密环境变量 + 文件锁
- 飞书 Hook 登录:从"生硬"到"无感"
- SSO 平台迭代:Poll 授权模式
- 业务 CLI 集成:最后一公里的安全闭环
- 与传统方案对比:差距在哪
传统 API Key 为什么在 Agent 场景下翻车
在 Web 时代,SSO 鉴权天然是"人在浏览器前"的模式:跳转到 SSO 平台 → 用户手动登录 → 回调拿 Token → 后续请求带着 Token。整个流程依赖人类的交互能力。
Agent 不行。Agent 没有浏览器,不会点按钮,无法完成交互式登录。
早期实践中最常见的做法是"预置 Token":开发者手动完成 SSO 登录,把长期 Token 写进环境变量、配置文件、甚至硬编码到代码里。这在个人玩具项目里没问题,但在企业环境下是灾难:
问题一:Token 大面积暴露。Token 以明文形式存在于 .env、config.yaml、shell history、/proc/*/environ 等任何你能想到的地方。任何能读取文件系统的攻击者都能拿到。
问题二:多人共享沙箱身份混乱。一个团队的 Agent 沙箱里,产品同事 A 登录后存了 A 的 Token,研发同事 B 紧接着调用——没有用户隔离的话,B 的请求用的是 A 的身份。审计日志显示是 A 在操作,但实际是 B。权限控制形同虚设。
问题三:无法追溯具体 Agent 行为。当 Agent 代替人做了某个操作,审计系统只知道"这个 Token 做了这件事",不知道是哪个用户、在什么对话上下文中、让哪个 Agent 执行的。出了问题,根本没法溯源。
这三个问题指向同一个结论:必须为 Agent 设计一套原生的、脱离浏览器依赖的、多人隔离且防泄露的 SSO 鉴权体系。
四层架构:sso-cli、业务 CLI、sso-sdk、SSO 平台
货拉拉的方案把鉴权拆成四个组件,各司其职:
MERMAID_BLOCK_0
sso-cli:独立的命令行工具,负责 SSO 登录编排、凭证安全存储、多用户隔离。不提供 Token 直接读取接口——你没法通过它拿到明文 Token。
业务 CLI:各业务团队开发的命令行工具(abtest-cli、trade-cli 等),Agent 实际调用的就是这些。它们不直接处理 Token,而是依赖 sso-sdk。
sso-sdk:内部私有包(Go),只有授权的模块才能引用。封装了从本地安全存储读取、解密 Token 的逻辑。
SSO 平台:公司统一单点登录服务,新增了 Agent 场景的 poll 授权模式——支持下发一次性 code,客户端轮询换 Token。
一次典型调用流:
- Agent 根据用户意图调用业务 CLI
- 业务 CLI 通过 sso-sdk 获取当前用户的 SSO Token
- Token 不存在或过期 → CLI 报错 → Agent 触发 sso-cli 登录
- sso-cli 向 SSO 平台申请一次性 code,通过飞书卡片让用户授权,后台轮询换 Token
- Token 加密写入本地,飞书卡片自动删除
- Agent 再次调用业务 CLI,正常返回结果
安全存储:Keychain 主密钥 + 加密文件
这是整套方案里我觉得最精巧的部分。
主密钥(Master Key) 存储在操作系统的原生密钥链中:
- macOS → Keychain
- Linux → Secret Service / gnome-keyring
- Windows → Credential Manager / DPAPI
主密钥不落在任何普通文件里,读取受操作系统用户权限保护。sso-cli 只在需要加解密时从 keychain 取出主密钥,用完立即从内存擦除。
Token 存储走的是"密文文件"路线:用户通过 SSO 登录拿到 Token 后,sso-cli 将 user → token 的映射序列化、用主密钥加密、写入本地文件。没有主密钥,拿到密文也解不开。
这个设计的核心假设是:沙箱环境可能被任何人读取文件系统,但攻击者通常无法直接访问运行中进程的操作系统密钥链(除非已经提权)。这就形成了两道防线:
攻击者拿到密文文件 → 没有主密钥 → 无法解密
攻击者拿到主密钥 → 需要操作系统 keychain 权限 → 需要提权
攻击者同时拿到两者 → 文件内多用户 key 粒度隔离 → 只能解密特定用户的 Token
对比传统方案把 Token 塞进 .env 文件或者 export TOKEN=xxx——差距不是一点半点。
多用户隔离:加密环境变量 + 文件锁
这是解决"多人共用沙箱"问题的关键设计。
每一轮 Agent 对话对应一个"当前用户"。Agent 框架启动子进程时,注入一组加密的临时环境变量来标识用户身份——不是明文用户名,而是经过对称加密的 salt。只有持有派生密钥的组件(sso-sdk)才能解密还原。
这组环境变量的安全属性:
- 生命周期绑定对话:对话结束立即销毁
- 不进 shell history:进程私有的环境块,不写入
/proc的全局视图 - 密文传输:即使被读取,看到的也是密文,无法知道当前用户是谁,更无法篡改为其他用户
并发控制通过文件锁保护:读操作并发(解密幂等),写操作加排他锁(避免登录写入竞态)。
这个设计的巧妙之处在于:它把"用户隔离"和"Token 隔离"合并在了一套机制里——同一个加密文件,通过 key 粒度区分不同用户,而不是每个用户一个文件。简化了并发控制,也减少了密文数量。
飞书 Hook 登录:从"生硬"到"无感"
安全做到位了,但初版的登录体验极其生硬:Agent 提示用户去浏览器登录 → 用户登录后回来说"好了" → Agent 触发轮询 → 拿到 Token 继续。
问题很明显:打断对话流,依赖用户主动告知,经常因为忘记触发轮询而超时。
Hook 模式彻底改变了这个体验:
- sso-cli 被调用时,拦截自身的 JSON 输出,生成一张飞书卡片消息
- 通过飞书 Bot 发送给当前用户,卡片含 SSO 登录链接,仅本人可见
- 用户点击卡片,浏览器完成 SSO 授权
- sso-cli 后台自动轮询 SSO 平台,用一次性 code 换 Token
- 拿到 Token 后:加密存储 → 通知飞书 Bot 删除卡片(不留聊天记录痕迹)→ 返回成功
- Agent 无缝继续执行业务 CLI
用户唯一要做的就是点一下飞书卡片。整个流程对用户来说几乎是透明的。
降级方案也考虑了:飞书卡片不可用时,退化为普通文本消息发登录链接。消息可能留在聊天记录里,但链接本身不包含 Token,风险可控。
SSO 平台迭代:Poll 授权模式
传统 SSO 只支持实时 Web 回调,无法适配 Agent 的异步场景。货拉拉推动了 SSO 平台的关键迭代——Agent Poll 授权模式。
流程是这样的:
- sso-cli 向 SSO 平台请求一次性登录链接,平台同时返回绑定的短期 code
- 链接通过飞书卡片给用户
- 用户浏览器打开链接完成登录,链接立即失效
- sso-cli 间隔轮询 SSO 平台,用 code 换 Token
- 第一次成功换取后 code 标记已用,后续请求被拒绝
安全设计要点:
- 一次性链接 + 一次性 code:防止链接被重复利用。即使泄露,一旦被正确使用即作废
- code 定时失效:防止暴力轮询
- 轮询幂等:第一次成功后,后续请求因 code 失效被拒绝,无副作用
- 用户校验:授权用户和发起用户不一致时,sso-cli 拒绝并终止
这个 poll 模式的价值不只是服务于 Agent——它让 SSO 平台具备了支撑任何异步授权场景的通用能力。
业务 CLI 集成:最后一公里的安全闭环
业务 CLI 开发者不需要关心主密钥、加密文件、环境变量这些复杂细节。引入 sso-sdk,调用一个方法拿 Token,完事。
import "internal.example.com/sso-sdk"
func main() {
token, err := sso_sdk.GetToken()
if err != nil {
// Token 不存在或过期,返回特定退出码
// Agent 感知后触发 sso-cli 登录
os.Exit(42)
}
// 用 token 调用业务 API
callBusinessAPI(token)
}
sso-sdk 的权限控制是编译期的:包托管在内部 Go Module Proxy,仓库权限仅对授权模块开放。未经审查的业务 CLI 连 import 都做不到,编译直接失败。这比运行时控制更前置、更绝对。
存储与获取分离也是有意为之:sso-cli 负责登录(交互重、状态多、易出错),sso-sdk 负责读取(精简、可靠、无 IPC)。读取过程不经过网络,不经过进程间通信,纯本地解密,安全且快速。
逆向防护方面,团队也在考虑把 Token 获取逻辑编译为共享库(.so/.dylib)动态加载,或者用 CGO + 静态库内嵌并剥离符号,提升逆向分析成本。目前主要以私有包权限保证安全边界。
与传统方案对比:差距在哪
| 维度 | 传统 API Key / 预置 Token | 货拉拉 CLI/SSO 方案 |
|---|---|---|
| Token 存储 | 明文 .env / config / 环境变量 | Keychain 主密钥 + 加密文件 |
| Token 可见性 | 任何能读文件的人都能看到 | 常规途径完全不可见 |
| 用户隔离 | 无(共享同一 Token) | 加密环境变量 + key 粒度隔离 |
| 审计追溯 | 只知道"这个 Token"做了什么 | 每次调用绑定真实用户身份 |
| 登录体验 | 手动登录后复制 Token | 飞书卡片一键点击,无感知 |
| Token 过期 | 手动刷新或等报错 | 自动检测 + 触发重新登录 |
| 权限控制 | 运行时检查 | 编译期仓库权限控制 |
| 泄露风险 | 高(多处明文暴露) | 低(纵深防御,多层保护) |
核心差距在安全模型上:传统方案假设"环境是可信的",货拉拉方案假设"环境是不可信的"——沙箱可能被任何人读取,Token 必须在任何常规载体中都不可见。这是一个根本性的思维转变。
你的 Agent 鉴权还在用 API Key 硬编码?评论区聊聊你踩过的坑。觉得有参考价值就点个赞,让更多人看到这套方案。
参考文档与链接
原始文章
- 货拉拉技术 - 面向 Agent Skill 的 CLI/SSO 鉴权体系 — 掘金原文,完整技术细节
相关项目与背景
- 飞书 CLI 开源项目 — 飞书 CLI 将文档、审批、日历等能力封装为 Agent 友好的命令
- Claude Code — Anthropic 的 Agent CLI 框架,Skill 机制是其核心扩展方式
- OpenClaw — 开源 Agent 框架,支持通过 Skill 调用外部 CLI 工具
安全技术参考
- OAuth 2.0 Device Authorization Grant — IETF RFC 8628,设备授权流程,Poll 模式的理论基础
- OWASP API Security Top 10 — API 安全风险清单,包含 Broken Authentication 等核心威胁
- macOS Keychain Services — Apple 官方密钥链文档
- Linux Secret Service API — Linux 密钥管理标准
Agent 安全相关
- Anthropic: Building effective agents — Agent 架构设计最佳实践
- GitHub: addyosmani/agent-skills — Agent Skill 设计模式和最佳实践集合
作者: itech001
来源: 公众号:AI人工智能时代
网站: https://www.theaiera.cn/
每日分享最前沿的AI新闻资讯和技术研究。
本文首发于 AI人工智能时代,转载请注明出处。

浙公网安备 33010602011771号