全栈 AI 应用鉴权:模型接口不能直接暴露给前端
全栈 AI 应用鉴权:模型接口不能直接暴露给前端
做全栈 AI 应用时,很多原型会让前端直接调用模型接口,甚至把供应商 Key 放进浏览器环境变量。原型跑起来很快,但安全边界完全失守。只要用户能打开开发者工具,就能看到请求参数、接口地址,甚至盗用额度。AI 应用的鉴权设计,必须从第一天就把模型接口藏在后端。
全栈 AI 应用不是“前端加一个聊天框”这么简单。它需要用户身份、额度、审计、限流和数据隔离共同工作。
一、请求必须经过后端网关
sequenceDiagram
participant U as User
participant F as Frontend
participant B as Backend Gateway
participant M as Model Provider
U->>F: submit prompt
F->>B: authenticated request
B->>B: quota and policy check
B->>M: server-side model call
M-->>B: response
B-->>F: filtered result
后端网关的作用不是简单代理,而是统一执行安全策略。前端只拿业务 Token,模型供应商 Key 永远留在服务端。后端可以根据用户、组织、套餐、任务类型决定能否调用模型,以及调用哪个模型。
如果应用支持多租户,还要保证每个租户的配置和日志隔离。不能让 A 组织的用户通过请求参数指定 B 组织的知识库,也不能把不同租户的模型调用日志混在一个不可筛选的表里。AI 应用的数据泄露常常不是模型问题,而是网关权限模型太粗。
二、额度要按业务动作扣减
CREATE TABLE ai_usage (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
action TEXT NOT NULL,
model TEXT NOT NULL,
input_tokens INT NOT NULL,
output_tokens INT NOT NULL,
created_at TIMESTAMP NOT NULL
);
额度不要只按请求次数算。一次长文总结和一次短问答成本不同,用户上传附件后的检索、重排、生成也可能包含多个模型调用。更合理的方式是按业务动作记录,再在动作内部记录 Token、模型、耗时和结果状态。
扣费或限额要具备幂等性。前端重试、网络断开、后端超时都可能导致重复请求。如果每次进入接口都扣额度,用户体验会很差。可以引入 request_id,让同一个业务请求只结算一次,并在状态表里记录 pending、success、failed。
三、权限要绑定资源
async function canRunAiTask(userId: string, resourceId: string) {
const role = await getRole(userId, resourceId);
return role === "owner" || role === "editor";
}
AI 功能通常会读取文档、工单、客户记录或代码片段。因此鉴权不能只判断“用户是否登录”,还要判断用户是否有权访问被输入给模型的资源。否则一个普通成员可能通过 AI 总结接口读取本不该看到的内容。
权限检查应该发生在数据进入 Prompt 之前。不要先拼好上下文再过滤输出,因为模型已经看到了敏感数据。RAG 场景尤其要注意,检索阶段就必须带上租户和权限条件,不能检索全库后再让模型“不要泄露”。
四、审计日志要可追踪
{
"user_id": "u_1024",
"action": "summarize_ticket",
"resource_id": "ticket_7788",
"model": "default-fast",
"policy": "team_editor",
"status": "success"
}
AI 应用出问题时,团队需要知道谁在什么时候对哪个资源做了什么。审计日志不一定保存完整 Prompt,但至少要保存用户、资源、动作、模型、策略结果和状态。对于高风险场景,可以保存脱敏后的输入摘要和输出摘要。
日志也要有保留周期。保存太少无法排查,保存太多会带来隐私风险。工程上可以把业务审计、成本统计、调试日志分开存储,分别设置访问权限和过期策略。
五、总结
全栈 AI 应用的模型接口不能直接暴露给前端。后端网关要负责鉴权、额度、权限过滤、限流、审计和供应商密钥管理。
AI 能力越强,访问边界越要清楚。把安全做在模型调用之前,才是可上线的全栈 AI 架构。