Python后端AI专题28:多租户隔离不是加一个 tenant_id:三层权限防线
Python后端AI专题28多租户隔离不是加一个 tenant_id三层权限防线在表里加tenant_id只是数据建模隔离要靠每条入口、每次资源查询和每条检索 SQL 都使用它。KnowFlow 的防线分三层Token 建立身份Repository/Service 约束资源归属pgvector 在 Top K 前过滤任一层遗漏都可能泄露。本篇最终完整模块knowledge_bases.py前面的代码片段用于解释本次改动下面是本篇结束时可直接核对和替换的磁盘完整版本。from__future__importannotationsfromcollections.abcimportSequencefromuuidimportUUIDfromsqlalchemyimportselectfromsqlalchemy.ext.asyncioimportAsyncSessionfromapp.modelsimportKnowledgeBaseclassKnowledgeBaseRepository:All public resource operations require tenant identity explicitly.asyncdefget(self,tenant_id:UUID,knowledge_base_id:UUID,*,session:AsyncSession)-KnowledgeBase|None:statementselect(KnowledgeBase).where(KnowledgeBase.tenant_idtenant_id,KnowledgeBase.idknowledge_base_id,)returnawaitsession.scalar(statement)asyncdeflist(self,tenant_id:UUID,*,session:AsyncSession)-Sequence[KnowledgeBase]:statement(select(KnowledgeBase).where(KnowledgeBase.tenant_idtenant_id).order_by(KnowledgeBase.created_at,KnowledgeBase.id))returnlist((awaitsession.scalars(statement)).all())asyncdefcreate(self,tenant_id:UUID,name:str,description:str,*,session:AsyncSession,)-KnowledgeBase:knowledge_baseKnowledgeBase(tenant_idtenant_id,namename.strip(),descriptiondescription.strip())session.add(knowledge_base)awaitsession.flush()returnknowledge_base重复投递练习答案新增测试让 ready job 再执行一次并用 CountingStorage/Embedding 调用数证明它连原文件都不再读取firstawaitindexer.run(job.id)calls_after_firstembedder.calls reads_after_firststorage.get_calls secondawaitindexer.run(job.id)recordsawaitvectors.records_for(tenant_idtenant,knowledge_base_idkb)assertfirst.statussecond.statusreadyassertembedder.callscalls_after_firstassertstorage.get_callsreads_after_firstassertlen(records)first.total_chunksIndexer 入口增加jobawaitself.jobs.get(job_id)ifjob.statusready:returnjob当前 ingestion 套件4 passed in 0.38s。显式 reindex 不应偷偷复用 ready job而应创建带新 pipeline fingerprint 的版本化 job这样旧索引仍可服务和回滚。第一层Token 只提供身份不提供最终授权JWT 只保存sub/tid/jti/iat/exp/type不放知识库清单payload{sub:str(identity.user_id),tid:str(identity.tenant_id),jti:str(identity.token_id),iat:issued,exp:issuedexpires_delta,type:access,}签名防篡改过期时间限制寿命资源是否仍存在、用户是否仍 active要由数据库实时判断。客户端请求体没有tenant_id字段路由从已验证 Token 取得避免“传谁的 tenant 就看谁的数据”。密码使用带随机盐的 scrypt验证用hmac.compare_digest。日志不记录密码、JWT 或完整 Prompt。第二层Repository 签名强迫携带 tenant知识库 Repository 的完整模块from__future__importannotationsfromcollections.abcimportSequencefromuuidimportUUIDfromsqlalchemyimportselectfromsqlalchemy.ext.asyncioimportAsyncSessionfromapp.modelsimportKnowledgeBaseclassKnowledgeBaseRepository:All public resource operations require tenant identity explicitly.asyncdefget(self,tenant_id:UUID,knowledge_base_id:UUID,*,session:AsyncSession,)-KnowledgeBase|None:statementselect(KnowledgeBase).where(KnowledgeBase.tenant_idtenant_id,KnowledgeBase.idknowledge_base_id,)returnawaitsession.scalar(statement)asyncdeflist(self,tenant_id:UUID,*,session:AsyncSession)-Sequence[KnowledgeBase]:statement(select(KnowledgeBase).where(KnowledgeBase.tenant_idtenant_id).order_by(KnowledgeBase.created_at,KnowledgeBase.id))returnlist((awaitsession.scalars(statement)).all())asyncdefcreate(self,tenant_id:UUID,name:str,description:str,*,session:AsyncSession,)-KnowledgeBase:itemKnowledgeBase(tenant_idtenant_id,namename.strip(),descriptiondescription.strip(),)session.add(item)awaitsession.flush()returnitem单测用inspect.signature规定所有公开方法的第一个业务参数必须是 tenant_id。它不能证明 SQL 一定正确却能防止新增方法忘记设计隔离入口SQL/E2E 另行验证。第三层向量排名前过滤第 21 篇已经让DocumentChunk.tenant_id、knowledge_base 和 ready 状态进入 WHERE。不能因为 API 已鉴权就删除这一层内部调用、后台任务、以后新增路由都可能绕过原入口。安全需要纵深防御。为什么统一返回 404Alice 请求 Bob 的 UUID如果返回 403攻击者知道资源存在如果不存在返回 404、越权返回 404外部无法区分。响应中也不能包含 Bob 名称。已有跨租户知识库与会话 E2E 均检查这一点。404 不是万能安全日志和审计仍应记录“鉴权主体请求了不属于它的 ID”用于攻击检测只是不对客户端泄露。数据库约束能做什么、不能做什么复合索引(tenant_id, knowledge_base_id)提升过滤效率外键保证引用存在但普通外键无法自动保证 chunk 的 tenant 与 document 的 tenant 一致。可进一步使用 PostgreSQL RLS 或复合外键增强。当前课程把应用查询与测试作为主防线并明确 RLS 是可继续加强的生产项。本篇练习同租户跨用户与跨租户矩阵画一个 2×2 验收矩阵同租户/不同租户 × 本人会话/他人会话。为每格写预期 HTTP 状态再为“管理员可读同租户会话”设计最小 RBAC 变更说明角色应从哪里读取、Repository WHERE 如何变化、为什么不能只相信 JWT 中长期不变的roleadmin。下一篇会给出矩阵和 RBAC 答案并处理另一类共享状态并发请求下配额、幂等键、缓存和 Token 成本如何一起设计。

相关新闻

实验室样品全流程管控:CMA/CNAS 评审中样品流转的常见缺陷解析

实验室样品全流程管控:CMA/CNAS 评审中样品流转的常见缺陷解析

摘要:样品是检测工作的对象,样品管理覆盖接收、标识、存储、制备、流转、留样、处置完整链条,样品管控失效会直接导致检测结果失效。在 CMA 资质认定、CNAS 认可现场评审当中,样品模块属于必查板块,很多实验室硬件存储…

2026/10/3 2:10:43 阅读更多 →
Trae插件开发:用TaoToken统一Key打通IDE智能工作流

Trae插件开发:用TaoToken统一Key打通IDE智能工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 0:20:37 阅读更多 →
广州专业的免费建站5个坑:避开这些注意事项流量翻倍

广州专业的免费建站5个坑:避开这些注意事项流量翻倍

广州专业的免费建站5个坑:避开这些注意事项流量翻倍 网站做好了没人访问,这是无数新手在广州做“免费建站”时最崩溃的瞬间。你花了半个月时间,从域名注册到服务器部署,甚至把SSL证书都配好了,结果后台流量显示为0,或者只有几个蜘蛛。别急着怀疑自…

2026/10/2 19:00:17 阅读更多 →

最新新闻

Vue中video-player首次加载黑屏?彻底解决props异步传值播放器不播放问题

Vue中video-player首次加载黑屏?彻底解决props异步传值播放器不播放问题

做 Vue 开发的同学,估计都跟 video-player 这个视频插件打过交道。它本质上是 Video.js 的 Vue 封装,用起来省事,一个组件就能把播放器渲染出来。但有个问题几乎每个人都会碰到:父组件通过 props 把视频地址传给子组件&#xff0c…

2026/10/4 6:04:12 阅读更多 →
WorkBuddy实战:用Skill链实现竞品动态追踪自动化

WorkBuddy实战:用Skill链实现竞品动态追踪自动化

1. 这不是一份“指南”,而是一份真实办公现场的作战地图你有没有过这样的时刻:早上九点刚坐定,邮箱里塞满跨部门协作需求,会议日程排到下午四点,手头还压着三份要当天交付的材料——其中一份是给市场部的竞品分析PPT&a…

2026/10/4 6:04:12 阅读更多 →
Magenta实战:用AI神经网络生成MIDI旋律的完整指南

Magenta实战:用AI神经网络生成MIDI旋律的完整指南

搞音乐编曲的朋友应该都有过这种体验:坐在MIDI键盘前,脑子里明明已经盘旋着一句旋律,但手一放到琴键上就卡壳,弹三遍全走样。或者更常见的,轨道铺好了、和弦也差不多了,就差一段旋律线,可怎么憋…

2026/10/4 6:04:12 阅读更多 →
Claude Desktop 配置 Whois MCP 实现 whois 查询:TaoToken 统一 Key 接入与 claude_desktop_config.json 验证

Claude Desktop 配置 Whois MCP 实现 whois 查询:TaoToken 统一 Key 接入与 claude_desktop_config.json 验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 6:04:12 阅读更多 →
AI 提示词  token  codex 想爆款标题:把 Codex auth.json 改到 TaoToken 的实操大纲

AI 提示词 token codex 想爆款标题:把 Codex auth.json 改到 TaoToken 的实操大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 6:04:12 阅读更多 →
FanControl 配置思路:让机箱风扇同时响应 CPU 和显卡负载

FanControl 配置思路:让机箱风扇同时响应 CPU 和显卡负载

机箱风扇只跟着 CPU 温度转,在游戏里未必合适。游戏可能主要让显卡发热,CPU 温度却不高;如果机箱进出风仍按 CPU 的曲线执行,显卡需要的风量就未必能及时跟上。 FanControl 能把 CPU、GPU 等温度来源和风扇控制集中到一个 Window…

2026/10/4 6:03:11 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →