21 · 权限系统对齐报告(企业文档 Agentic RAG 权限工程实践对标)#
目的:审查 DocMind 现有权限/访问控制系统,对标主流企业文档 Agentic RAG(Glean、Microsoft Azure AI Search / Copilot、Google Vertex AI Search / Agentspace、Milvus 多租户、MCP 2025-11 规范、OWASP LLM Top 10 2025)的真实工程实践,给出一份如何对齐的报告。
配套:本篇是 19-对标主流产品改造报告 「P2-2 权限下推」与 20-改造实现设计 「Stage D(暂缓)」的深化。15-RBAC 权限系统升级方案 是应用层 RBAC 的落地设计,本篇负责把它与”检索层权限”和”Agentic/MCP 安全”统一收口。
调研时间:2026-06。来源见文末。
0 · 执行摘要#
DocMind 当前的权限模型是 「JWT 认证 + 单 role 字段 + URL 前缀拦截」的简化版,且存在一个被设计文档忽略的关键事实:RBAC 四张表(sys_role/sys_permission/sys_user_role/sys_role_permission)已在数据库建好并灌入种子数据(3 角色 / 15 权限码),但应用层一行都没读——UserDetailsServiceImpl 仍只把 sys_user.role 单字段拼成 ROLE_xxx。即 DB 层 RBAC ≠ 应用层 RBAC,二者完全脱节。
与主流企业 Agentic RAG 对照,最核心的差距只有一条:
主流是”权限在检索层强制(permission-at-retrieval)“——ACL 与内容一起索引,查询期按用户身份过滤,受限内容根本不进入 LLM 上下文(“The LLM cannot leak what it never receives”)。DocMind 是”权限只在 UI/接口前缀挡一下”——检索层完全无用户隔离,
kbIds由请求参数任意指定且不校验归属,MCP 端点裸奔无鉴权。
但在动手前,有一个必须先做的产品决策(也是 [20] 把 Stage D 暂缓的真正原因):
DocMind 的知识库到底是”全局共享单语料库”还是”按用户/团队隔离”? 现状代码是全局共享(
KnowledgeBaseController.list不按 user_id 过滤,种子数据全归user_id=1,管理员写、所有人读)。“按 owner 隔离”是 [15] 的设计假设,但与现状冲突。这个语义不定,“kbIds 归属校验”就无从谈起。
核心结论(按优先级)#
| 优先级 | 对齐项 | 一句话 | 是否依赖产品决策 |
|---|---|---|---|
| P0 | MCP 端点鉴权 | /mcp/** 裸奔 → 至少 API-Key/JWT 拦截,对齐 MCP OAuth2.1 资源服务器范式 | 否 |
| P0 | Memory userId 收口 | recall/store_memory 不再接受外部传 userId,强制从 auth context 注入 | 否 |
| P0 | RBAC 接线(DB→应用层) | 把已建的四表接进 UserDetailsService/@PreAuthorize,消灭”建了不用” | 否 |
| P0 | 决策:共享 vs 隔离 | 给 kb_knowledge_base 加 visibility,定义 KB 可见性语义 | 是(先决) |
| P1 | 检索层权限下推 | ACL 字段与 chunk 同索引,检索期按用户/可见性过滤(security trimming) | 是 |
| P1 | Agentic 工具最小权限 + provenance | 工具 read-only、按用户裁剪、chunk 打来源/信任标签,防 confused deputy | 部分 |
| P2 | 审计 + 实时权限 + 向量库多租户 | sys_audit_log、权限热更新、Milvus partition-key/collection 隔离 | 是 |
1 · 现状盘点(诚实清单,带代码位置)#
1.1 认证 Authentication —— 基本健全#
- JWT(HMAC SHA-256),claims =
userId / username / role(单值字符串),默认 24h,JwtUtils.java。 - JwtAuthenticationFilter.java:token 优先取
Authorization: Bearer,其次 URL?token=(SSE 流式接口需要)。 - 白名单 SecurityConfig.java:48-61:login/register/refresh/forgot、swagger、actuator、uploads、
/sse,以及致命的/mcp+/mcp/**(完全放行,无任何鉴权)。 - userId 一律由
userService.getCurrentUserId()从 SecurityContext 派生(✓ 正确,会话类接口都做了conv.getUserId().equals(userId)归属校验)。
1.2 授权 Authorization —— RBAC 表”建了不用”#
- 关键发现:UserDetailsServiceImpl.java:44 仍是
即权限完全来自
java.authorities(List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole().toUpperCase())))sys_user.role单字段。docs/docmind.sql:274-385 已建好sys_role/sys_permission/sys_user_role/sys_role_permission四表 + 3 角色(SUPER_ADMIN/KB_ADMIN/USER)+ 15 权限码 + 映射,但没有任何PermissionService去读它们。[15] 的 Phase 2~4(perms 进 JWT、hasAuthority('kb:upload')、@OwnerCheck切面、热更新)均未实施。 - 鉴权点:SecurityConfig.java:73
/api/admin/** → hasRole("ADMIN");外加散落的@PreAuthorize("hasRole('ADMIN')")(KB 写操作 KnowledgeBaseController.java:31/49/87/97/108/118、用户管理、统计)。全是硬编码'ADMIN'字面量的二元判断。
1.3 知识库(KB)级权限 —— 全局共享,读侧无隔离#
kb_knowledge_base有user_id(仅记录创建者),无visibility/owner_acl/shared_with字段(docs/docmind.sql)。- KnowledgeBaseController.java:65-71
list→listKnowledge(current,size,category,status),不传 userId → 任意登录用户拿到全部 KB 列表。 - DocMindChatController.java:74-84:
kbIds来自请求参数KbIdsParser.parse,userId 从 JWT 派生(✓),但二者从不交叉校验 → 用户可传kbIds=任意值检索他人 KB。 - 净语义:管理员写、所有登录用户读全部——一个全局共享语料库。写操作(upload/url/delete/reprocess)已被
hasRole('ADMIN')保护,但读/检索完全放开。
1.4 文档 / chunk 级权限 —— 零#
kb_chunk无任何 ACL 字段(docs/docmind.sql);Milvus collection 不存 user_id/tenant。- VectorRetriever / BM25Retriever 仅按
kbIds + category过滤,无 per-user 过滤、无 security trimming。
1.5 MCP 工具权限 —— 内部安全、外部裸奔#
- 6 工具(doc_search/keyword_search/web_search/recall_memory/store_memory/kb_meta)经
spring-ai-starter-mcp-server-webmvc在/mcp暴露,外部端点无鉴权。 - 内部路径安全:agentic 循环 / Worker 调用时,AgentToolContext 在
isActive()时强制用会话 kbIds 覆盖 LLM 传入(DocSearchTool.searchDocs),且 agentic 白名单硬排除store_memory/kb_meta——这是做得很好的防御设计。 - 外部路径越权:直接 POST
/mcp时,kbIds以调用方传入为准(无限制);MemoryTool.resolveUserId 优先用外部传入的userId→ 可读写任意用户的记忆。
1.6 Memory 权限 —— 逻辑隔离对、信任源错#
- MemoryStore:Redis key 前缀
user:memory:v2:{userId}+ Milvususer_id == "..."过滤,逻辑隔离正确。 - 但外部路径 userId 来自参数而非 auth → 隔离形同虚设(见 1.5)。
越权风险点汇总#
| 风险点 | 位置 | 影响 | 严重性 |
|---|---|---|---|
/mcp/** 无鉴权 | SecurityConfig:59-60 | 匿名调 6 个工具 | 高 |
| 外部 MCP 可传任意 kbIds | DocSearchTool(非 active 分支) | 读任意 KB | 高 |
| 外部 MCP 可传任意 userId 读写记忆 | MemoryTool.resolveUserId | 串改他人记忆 | 高 |
kbIds 请求参数无归属校验 | DocMindChatController:74 | 登录用户读他人 KB | 中(取决于共享语义) |
| KB 列表不按 user 过滤 | KnowledgeBaseController:65 | 列举全部 KB | 中(同上) |
| 检索层无 per-user 过滤 | VectorRetriever/BM25Retriever | 无法做 security trimming | 中 |
| RBAC 四表建了不用 | UserDetailsServiceImpl:44 | 细粒度授权失效、维护误导 | 中 |
注:4/5 的”严重性”取决于 §4 的共享 vs 隔离决策——若产品定位就是”全局共享 demo”,它们不算漏洞;若要按用户隔离,它们是高危。这正是必须先决策的原因。
2 · 主流企业文档 Agentic RAG 的权限工程实践(调研结论)#
范式 A:权限在检索层强制(permission-at-retrieval / security trimming)——最核心共识#
三大主流产品的实现高度一致:ACL 检查发生在内容进入 LLM 之前。
- Glean:索引期同时爬”内容 + ACL”,查询期先召回再按用户权限过滤,只把”安全子集”喂给 LLM——“The LLM cannot leak information it never receives”。强调实时权限(源系统权限一变,搜索结果立刻更新)+ 最小权限。
- Azure AI Search:query-time security trimming。两条路线:(1) 2025-05 起原生 Entra ACL/RBAC;(2) 通用 security string filter——索引里建
group_ids可过滤字段,查询期 ODatagroup_ids/any(g: search.in(g, '用户的组'))。这条最适合非微软体系、可直接照搬到 Milvus 标量过滤。 - Vertex AI Search / Agentspace:JSONL 里带
acl_info、data store 设aclEnabled:true,查询期把认证主体(google.subject/group)匹配文档 ACL,同一 query 不同用户结果不同。明确警告:别依赖 UI 层过滤做安全;必须后端强制 + 严格 metadata filter。
学术界同样收敛到此:要”确定性消除 RAG 泄露”,必须在任何内容喂给 LLM 前显式校验所有相关方的访问权限(arXiv 2509.14608)。
范式 B:ACL 与内容同索引 + 身份只来自 auth context#
- 把”谁能看”(principal/group ids)作为可过滤字段与 chunk 一起入库,而非检索后再查库。
- 身份只能来自认证上下文,绝不接受前端/模型/调用方传值;从后端服务发起检索(backend mediation),不要让浏览器直连检索引擎。
范式 C:向量库多租户(Milvus 四级隔离)#
| 级别 | 隔离强度 | 上限 | RBAC | 适用 |
|---|---|---|---|---|
| Database | 最强(物理) | 64 | ✓ | 强合规、少租户 |
| Collection | 强(物理) | 65536 | ✓ | 强隔离、中等租户 |
| Partition | 中 | 1024/coll | ✗ | 聚合分析 |
| Partition Key | 弱(逻辑) | 百万级 | ✗ | 海量租户、性能最好 |
- 关键安全事实:RBAC 只在 DB/Collection 级生效;partition-key 是逻辑隔离,安全完全依赖应用层每次查询都正确带上过滤条件——查询构造一旦出 bug 就跨租户泄露。
- 最简单的”单 collection + tenant 字段过滤”可行,但过滤性能可能成为 ANN 瓶颈(Milvus 官方明确提示)。
- 运维红线:Milvus metrics 端口 9091 未授权访问漏洞(GO-2026-4481),勿暴露、勤打补丁。
范式 D:MCP 安全(2025-11 规范已成熟)#
- MCP server = OAuth 2.1 资源服务器;PKCE 强制;认证委托外部 IdP,server 只验 token(issuer/audience/expiry/scope)。
- Resource Indicators(RFC 8707) 绑定 token 到特定 server,防跨服务盗用;RFC 9728 PRM 发现端点
/.well-known/oauth-protected-resource。 - 2026 起支持角色化工具访问(
@RolesAllowed限制单个工具)。 - 内部/服务间场景:可用 mTLS / M2M client credentials 替代用户登录流。
- 真实事故:OAuth proxy 共享
client_id→ consent 缓存绕过(Obsidian Security, 2025)。绝不能匿名暴露工具端点——正是 DocMind 现状。
范式 E:Agentic 特有风险(OWASP LLM Top 10 2025)#
- LLM01 Prompt Injection:Agentic RAG 尤其暴露于间接注入(恶意指令藏在被检索的文档里);每一轮迭代检索都是一次新的注入机会,risk 比单轮 RAG 更高。投毒 5 篇文档即可 90% 成功率操纵答案。
- LLM06 Excessive Agency + Confused Deputy:Agent 持有用户没有的”高权”(API key/DB 访问),被注入操纵后替攻击者越权行事;根因是 LLM 无法在架构层区分”控制指令 vs 数据”。
- 防御共识(控制必须在模型之外):
- 工具最小权限:默认 read-only,按”请求用户自身的权限”裁剪工具能力(“a tool’s access must be limited to what the requesting user is allowed to do”),不要让工具跑在比用户更高的服务账号权限上。
- provenance 标记:每个检索 chunk 打”来源 + 信任级别”标签随上下文传下去,下游据此施加不同审查;“把模型输出当不可信输入”。
- action budget、高危操作 human-in-loop、对抗性渗透测试。
DocMind 已做对的两件事,正好命中此范式:① agentic 循环只暴露 3 个 read 工具、硬排除
store_memory(写工具走DocMindAgent.stageMemoryWrite()直连 Java);②AgentToolContext强制覆盖 kbIds(防 LLM 篡改)。这是教科书式的”最小权限 + 模型外强制”,应在面试中重点讲。
3 · 差距对照表#
| 维度 | 主流怎么做 | DocMind 现状 | 差距 |
|---|---|---|---|
| 权限强制位置 | 检索层(content 前置过滤) | UI/接口前缀 | 检索层零隔离 |
| ACL 存储 | 与内容同索引的可过滤字段 | 无 | 缺字段 |
| 身份来源 | 仅 auth context | 内部✓ / 外部 MCP 接受传值 | 外部路径可伪造 |
| KB 可见性语义 | owner/group ACL + 实时 | 全局共享、无 visibility | 语义未定义 |
| 应用层授权 | RBAC/ABAC 细粒度 | 单 role + 硬编码 ADMIN(四表建了不用) | RBAC 脱节 |
| MCP 端点 | OAuth2.1 资源服务器 | 匿名放行 | 裸奔 |
| Agentic 工具 | 最小权限 + provenance | 工具最小权限✓ / 无 provenance | 部分对齐 |
| 向量库租户 | DB/Collection/PartitionKey | 单 collection 无 tenant 字段 | 无多租户 |
| 审计 | 全量审计 + 实时回收 | 无 | 缺失 |
4 · 对齐方案(分层 · 优先级 · 决策点)#
每条结构:主流怎么做 → 现状 → 怎么改 → 收益 → 风险 → 落地文件。先决策、再速赢、后下推。
【决策点 · 先决】KB 可见性语义:共享 vs 隔离#
这是 [20] Stage D 暂缓的根因,必须先拍板。给 kb_knowledge_base 加一个 visibility 字段统一三种形态,存量数据默认 PUBLIC 保持现状不破坏 demo:
| 选项 | 语义 | 适用 | 改动量 |
|---|---|---|---|
| A 全局共享(现状) | 所有人读全部 | 纯演示,定位”统一企业知识库” | 仅补 MCP 鉴权即可,不动检索 |
| B 共享 + 私有(推荐) | visibility ∈ {PUBLIC, PRIVATE},PUBLIC 全员可读、PRIVATE 仅 owner | demo 可演示”隔离”又不破坏存量 | 中:加字段 + 检索过滤 + 列表过滤 |
| C 严格按 owner/team 隔离 | 每 KB 归属 user/team,跨域需显式授权 | 真多租户 SaaS | 大:ACL 表 + Milvus 多租户 |
推荐 B:一个
visibility列 + 检索期WHERE visibility='PUBLIC' OR user_id=:me就能既对齐 security trimming 范式、又演示隔离能力、还零破坏存量,性价比最高。面试讲”为什么不直接上 C”——ACL 矩阵对 demo 过重,先用 visibility + owner 覆盖 80% 场景,留 C 作为多租户演进方向。
【P0 · 速赢,不依赖决策】#
P0-1 MCP 端点鉴权(范式 D)#
- 现状:
/mcp、/mcp/**在 SecurityConfig.java:59-60 白名单裸奔。 - 怎么改:从白名单移除
/mcp/**,加 JWT 或独立 API-Key 过滤(MVP 阶段 API-Key 即可,注释里讲清”生产应升级为 OAuth2.1 资源服务器 + RFC 8707 资源指示符”对齐主流)。 - 收益:堵掉最高危的匿名越权;面试可讲 MCP 2025-11 安全演进。
- 风险:外部 MCP 客户端需带凭证——demo 无外部消费者,无影响。
- 落地:
SecurityConfig.java(+ 可选McpApiKeyFilter)。
P0-2 Memory userId 收口(范式 B)#
- 现状:MemoryTool.resolveUserId 优先用外部传入 userId。
- 怎么改:
recall/store_memory的 userId 只从AgentToolContext取,忽略/拒绝外部传值(外部 MCP 无 active context 时直接拒绝记忆类工具)。 - 收益:消灭跨用户记忆读写。风险:无(内部路径本就用 context)。
- 落地:
MemoryTool.java、MemoryStore调用点。
P0-3 RBAC 接线:让”建了的四表”真正生效(对齐 [15])#
- 现状:UserDetailsServiceImpl.java:44 只读
sys_user.role。 - 怎么改:实现 [15] Phase 2 —— 新增
PermissionService.loadPermissionCodes(userId)(查四表 + Redis 缓存),UserDetailsService把权限码作为 authority,散落的hasRole('ADMIN')渐改hasAuthority('kb:upload')等。JWT 可塞 perms([15] §3.1,注意体积降级)。 - 收益:消灭”建了不用”的认知负债;细粒度、可审计、可热更新;面试 STAR 现成(影响 7 controller/30+ 接口)。
- 风险:注解散落易遗漏 → 用 ArchUnit 强制 controller 每个 public 方法必须有
@PreAuthorize/@PermitAll([15] §8)。 - 落地:见 [15] §5 清单。
【P1 · 检索层权限下推(依赖决策 B/C)】#
P1-1 Security trimming:ACL 字段与 chunk 同索引(范式 A+B)#
- 主流:Azure
group_ids/any(g:search.in(...))、Vertexacl_info。 - 怎么改(选 B 时最轻):
kb_chunk与 Milvus collection 增owner_id+visibility标量字段(随切片写入,从所属 KB 继承)。- VectorRetriever/BM25Retriever 检索时强制注入 Milvus 过滤表达式
visibility == "PUBLIC" || owner_id == {ctxUserId}(userId 来自AgentToolContext,模型不可篡改——复用现有 kbIds 覆盖范式)。 kbIds归属校验下推:抽KbAccessGuard.filterOwned(userId, kbIds)([20] Stage D 已设计),在DocMindChatController.stream入口剔除越权 kbId。KnowledgeBaseController.list传 userId,列表只回”PUBLIC + 自己的 PRIVATE”。
- 收益:受限内容根本不进 LLM 上下文,对齐主流抗泄露架构;语义/答案缓存的跨用户风险也一并解决。
- 风险:Milvus 标量过滤性能(范式 C 提示)→ 数据量大时考虑 partition-key by owner。
- 落地:
docmind.sql、MilvusService、两个 Retriever、DocMindChatController、新增KbAccessGuard。
【P1 · Agentic 工具最小权限 + provenance(范式 E)】#
- 已对齐:read-only 工具白名单、
store_memory硬排除、kbIds 强制覆盖(保留并在面试强调)。 - 补强:① 检索 chunk 注入 provenance(source/trust level)随 prompt 传下,KB 内部内容与 web 抓取内容信任级别区分(web 内容更易藏间接注入);② 给 agentic 循环加 action budget(已有
max_iterations,可补”重复 query 去抖”);③ 文档摄入期对 web/上传内容做基础注入检测(可选)。 - 收益:抗间接 prompt injection / confused deputy;面试可讲 OWASP LLM01/06。
- 落地:
SourcePayloadFactory/PromptAssembler(provenance 字段)、AgenticSearchOrchestrator。
【P2 · 企业级加固(按运营需要)】#
- 审计:
sys_audit_log+ AOP([15] Phase 4),记录登录/角色变更/越权拒绝/MCP 调用。 - 实时权限(范式 A 的”killer feature”):Redis
user:perm:revoke:{id}=ts,JWT 校验比对签发时间([15] §3.5),权限变更分钟级生效。 - 向量库多租户演进(范式 C,仅选 C 时):单 collection +
owner_id标量过滤 → 数据增长后迁 partition-key by tenant;强合规场景上 collection 级 + Milvus RBAC。修复 9091 端口暴露。
5 · 落地顺序(先堵洞、再决策、后下推)#
- P0-1 / P0-2(MCP 鉴权 + Memory 收口)——独立、低风险、堵高危洞,先做。
- 决策点(共享 vs 隔离,推荐 B)——产品/自己拍板,gate 后续。
- P0-3 RBAC 接线——独立于决策,可并行,落地 [15]。
- P1-1 检索层权限下推——依赖决策 B/C,是与主流对齐的核心一刀。
- P1 Agentic provenance + P2 审计/实时/多租户——按需排期。
6 · 面试讲故事角度#
- “权限为什么不只在接口挡,要下推到检索层?” —— 因为 Agentic RAG 里 LLM 会把召回内容综合进答案,接口挡不住”内容已进上下文”;主流(Glean/Azure/Vertex)一致做法是 permission-at-retrieval——“模型收不到就泄不出”。我把 ACL 作为可过滤字段与 chunk 同索引,检索期按 auth-context 注入过滤。
- “你怎么防 Agent 越权(confused deputy)?” —— 三层:① 工具最小权限(只暴露 3 个 read 工具、写工具不给 LLM);② kbIds/userId 由
AgentToolContext强制覆盖,模型传啥都不算数;③ chunk 打 provenance,区分 KB 与 web 信任级。控制全在模型之外。 - “RBAC 你不是建表了吗?” —— 诚实讲:DB 层四表先行落地,但应用层接线(PermissionService/
hasAuthority/OwnerCheck 切面)是这次对齐补齐的——“建了表 ≠ 有了权限系统”,这正是审查发现的认知负债。 - “为什么不直接上多租户/ACL 矩阵?” —— 用
visibility + owner覆盖 80% 场景,ACL 矩阵对当前规模过重;留 partition-key 多租户作为演进方向(YAGNI + 可演进)。
6.5 · 实施状态(2026-06-07 落地)#
决策已拍板:内部系统,不上多租户;功能权限走 RBAC、数据权限按「KB 级 + 部门 + 公开/私有」;上传默认 PUBLIC(沿用现状全员可读),存量一律 PUBLIC(零破坏)。
数据权限部分已实施(mvn test:129 单测全过,唯一失败 contextLoads 为 Milvus 基础设施未启动,与改动无关):
| 项 | 落地 |
|---|---|
| Schema | docmind.sql 追加:sys_dept(4 部门种子)+ sys_user.dept_id + kb_knowledge_base.dept_id/visibility;存量 UPDATE 归部门、可见性默认 PUBLIC |
| 实体 | SysUser.deptId、KbKnowledgeBase.deptId/visibility |
| 收口类 | 新增 support/KbAccessGuard:resolveAccessible(deptId) / filterAccessible(deptId, requested) / assertAccessible,规则 visibility='PUBLIC' OR dept_id=:dept |
| 聊天入口 | DocMindChatController.stream 进 agent 前 filterAccessible 收敛 kbIds(空≠全库,改为「全部可访问」) |
| 列表 | listKnowledge 增 deptId 入参 + 部门过滤;UserService.getCurrentUserDeptId() |
| 零改动 | VectorRetriever/BM25Retriever/MilvusService/AgentToolContext/JwtUtils —— KB 级粒度只需收敛 kbIds,检索链自动只在授权范围召回(security trimming) |
P0 续:RBAC 接线已实施(让已建四表真正驱动鉴权):
| 项 | 落地 |
|---|---|
| 权限解析 | 新增 service/PermissionService:user→role→permission 三段式,产出「权限码 + ROLE_<角色码>」,无 RBAC 映射时按 legacy sys_user.role 兜底 |
| 认证接线 | UserDetailsServiceImpl 改为加载 PermissionService 授权 + 保留 legacy ROLE_<role>(/api/admin/** 兼容);JwtAuthenticationFilter 走 UserDetailsService 故全链自动生效 |
| 鉴权迁移 | hasRole('ADMIN') → hasAuthority('<perm>'):KB 写操作 kb:upload/delete/reprocess、Stats stats:read、User user:read/user:manage(AiConfig 仍由 /api/admin/** URL 守卫,兼容保留) |
| 验证 | 活动 DB 实测:admin→ROLE_SUPER_ADMIN+全 15 权限码(可上传/删除/管理用户);simon→ROLE_USER+仅 kb:read/chat:use/chat:export(上传 403)。行为同改造前但已由 RBAC 表驱动 |
P0 续:MCP 端点鉴权已实施:
| 项 | 落地 |
|---|---|
| 过滤器 | 新增 security/McpApiKeyAuthFilter:/mcp/**、/sse 校验 X-API-Key(定长比较),合法注入 ROLE_MCP_CLIENT,缺失/错误→401;配置键 docmind.mcp.api-key(默认 dev key,env MCP_API_KEY) |
| 收口 | SecurityConfig 从白名单移除 /mcp//mcp/**//sse,注册该过滤器;内部 agentic 路径走 Java 直调 @Tool,不受影响 |
| 测试 | McpApiKeyAuthFilterTest(4:合法 key 认证 / 缺 key 匿名 / 错误 key 匿名 / 非 MCP 路径不处理) |
P0 续:Memory userId 收口 + 上传归属部门 已实施:
| 项 | 落地 |
|---|---|
| ① Memory userId 收口 | MemoryTool.resolveUserId 改为只取 AgentToolContext 的 userId,彻底忽略外部/LLM 传入;无上下文(外部 MCP 直连)→ 空 → 记忆操作 no-op。recall/store_memory 的 userId @ToolParam 标注「自动注入,传入忽略」。测试 MemoryToolTest 更新为 context 注入并断言外部 attacker-id 被忽略(7 过) |
| ④ 上传归属部门 | uploadDocument/addUrlDocument 增 deptId 入参,KnowledgeBaseController 从 getCurrentUser() 注入上传者部门;visibility 仍由 DB 默认 PUBLIC 兜底(沿用全员可读) |
仍待办(演进项,非必须):② MCP 升级 OAuth2.1 资源服务器(对外开放/合规时);③ PermissionService 接 Redis 缓存(流量上来后;当前实时查库反而无权限陈旧问题);④ 前端 PUBLIC/PRIVATE 可见性切换(仅”通过 UI 端到端建私有库”才需要,演示可 SQL 翻 visibility)。
可选后续:上传时设归属部门 + 前端可见性切换(当前默认 PUBLIC 故非必需);新注册用户默认部门(当前 null → 仅见 PUBLIC,因全 PUBLIC 故行为一致)。
7 · 同步面试文档(落地后)#
- 15-RBAC 权限系统升级方案 —— 标注”P0-3 接线”为本报告 P0,补”检索层权限下推”小节。
- 20-改造实现设计 —— Stage D 状态从”暂缓”更新为”按本报告决策 B 落地”。
- 01-项目全景与架构设计 / 05-工程化实践 —— 安全模块章节。
- 13-简历素材与 STAR 故事 —— 新增 STAR:检索层权限下推 + MCP 鉴权 + RBAC 接线。
附 · 调研来源#
- Glean, Secure generative AI requires the right permissions structure — https://www.glean.com/blog/secure-generative-ai-for-the-enterprise-requires-the-right-permissions-structure ↗ ;Best RAG features in enterprise search — https://www.glean.com/perspectives/best-rag-features-in-enterprise-search ↗ ;How security affects enterprise search — https://www.glean.com/perspectives/how-do-security-features-affect-enterprise-search ↗
- Microsoft, Document-level access control in Azure AI Search — https://learn.microsoft.com/en-us/azure/search/search-document-level-access-overview ↗ ;Security filter pattern (security trimming) — https://learn.microsoft.com/en-us/azure/search/search-security-trimming-for-azure-search ↗ ;Query-Time ACL and RBAC Enforcement — https://learn.microsoft.com/en-us/azure/search/search-query-access-control-rbac-enforcement ↗ ;Entra-based document-level security — https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/announcing-enterprise-grade-microsoft-entra-based-document-level-security-in-azu/4418584 ↗
- Google Cloud, Set up data source access control (Agent Search / Vertex AI Search) — https://docs.cloud.google.com/generative-ai-app-builder/docs/data-source-access-control ↗ ;Access control with IAM — https://docs.cloud.google.com/generative-ai-app-builder/docs/access-control ↗ ;Respecting ACLs in AgentSpace custom data sources — https://aashna-kunk.medium.com/agentspace-searches-on-custom-data-sources-respecting-acls-6b1a03fbc833 ↗
- Milvus, Implement Multi-tenancy — https://milvus.io/docs/multi_tenancy.md ↗ ;Use Partition Key — https://milvus.io/docs/use-partition-key.md ↗ ;Designing Multi-Tenancy RAG with Milvus — https://milvus.io/blog/build-multi-tenancy-rag-with-milvus-best-practices-part-one.md ↗
- MCP, Understanding Authorization in MCP — https://modelcontextprotocol.io/docs/tutorials/security/authorization ↗ ;Red Hat, MCP security: authentication and authorization — https://www.redhat.com/en/blog/mcp-security-implementing-robust-authentication-and-authorization ↗ ;Cloud Security Alliance, Agentic MCP Security Best Practices v1 — https://labs.cloudsecurityalliance.org/agentic/agentic-mcp-security-best-practices-v1/ ↗ ;Microsoft ISE, Secure MCP Server with OAuth 2.1 and Azure AD — https://devblogs.microsoft.com/ise/aca-secure-mcp-server-oauth21-azure-ad/ ↗
- OWASP, Top 10 for LLM Applications (2025) — https://aembit.io/blog/owasp-top-10-llm-risks-explained/ ↗ ;Christian Schneider, From LLM to agentic AI: prompt injection got worse — https://christian-schneider.net/blog/prompt-injection-agentic-amplification/ ↗ ;SANS, Your AI Agent Is an Easily Confused Deputy — https://www.sans.org/blog/your-ai-agent-easily-confused-deputy-why-cloud-security-needs-credential-broker/ ↗
- 学术:Enterprise AI Must Enforce Participant-Aware Access Control — https://arxiv.org/pdf/2509.14608 ↗ ;Taming Privilege Escalation in LLM-Based Agent Systems (MAC framework) — https://arxiv.org/abs/2601.11893 ↗