通过登录,系统可以知道当前请求来自哪个用户;通过 Gateway,系统可以统一校验 Token,并把用户身份透传给下游服务。
但是,仅仅知道“这个人是谁”还不够。
企业知识库真正关心的是另一个问题:
这个用户能不能访问这个资源?
比如:
- A 用户能不能查看 B 用户创建的知识库?
- 普通用户能不能查看所有用户的问答日志?
- 用户上传文档时,能不能往别人的知识库里上传?
- 用户提问时,检索范围是不是只在自己的知识库内?
- 管理员为什么可以查看全局任务?
这些问题都属于资源隔离和权限边界。
这一篇要讲清楚 KnowHub 如何在登录认证之后继续做用户资源隔离,以及基础后台管理端如何建立在 USER / ADMIN 角色之上。
01 登录认证不等于资源授权
很多初学者会把“登录”和“有权限”混在一起。
其实它们不是一回事。
登录认证解决的是:
你是谁?资源授权解决的是:
你能访问什么?用户成功登录,只能说明他是系统里的合法用户。它并不意味着这个用户可以访问所有知识库、所有文档和所有日志。
举个例子。
A 用户登录后,Token 中的身份是:
userId = 1 role = USERB 用户创建了一个知识库:
kbId = 100 userId = 2A 用户如果请求:
GET /kb/100Gateway 只能判断 A 用户已经登录,它并不知道 kbId=100 属于谁。
真正的判断必须在 knowledge-service 中完成:
查询 knowledge_base.id = 100 判断 knowledge_base.user_id 是否等于当前 userId 如果不相等,则拒绝访问这就是为什么企业系统不能只做登录,还必须在业务层做资源归属校验。
02 用户资源隔离的 4 个基本原则
KnowHub 的资源隔离遵循几个原则。
不相信前端传入的 userId
前端传来的参数都可以被用户修改。
所以用户侧接口不应该依赖前端传入的 userId,而应该使用 Gateway 透传后由下游服务构造出的当前用户身份。
也就是:
当前用户 = UserContext.getUserId()而不是:
当前用户 = request.getParameter(”userId”)所有用户侧查询都带当前 userId
查询“我的知识库”时,SQL 条件中必须包含当前用户 ID。
逻辑类似:
select * from knowledge_base where user_id = 当前用户ID and status = ENABLED这样 A 用户只能看到自己的知识库。
跨用户访问不要泄露资源信息
如果 A 用户访问 B 用户的知识库,系统可以返回无权限,也可以返回资源不存在。
很多系统会选择返回“资源不存在”,这样可以减少资源探测风险。
A 用户访问 /kb/100 但 kbId=100 属于 B 用户 系统返回:知识库不存在从用户体验看,这和真的没有这个知识库类似;从安全角度看,攻击者无法判断这个 ID 是否真实存在。
写操作更要先校验归属
不仅查询要校验,写操作更要校验。
比如:
- 上传文档前,先校验知识库是否属于当前用户。
- 删除知识库前,先校验知识库 owner。
- 发起问答前,先校验知识库 owner。
- 重建索引前,先校验文档是否属于当前知识库和当前用户。
只有这样,用户才不能把数据写进别人的资源里。
03 知识库 owner 校验怎么做
知识库是 KnowHub 中最核心的资源边界。
在 knowledge_base 表中,每条记录都应该有一个 user_id 字段。
它表示这个知识库属于哪个用户。
简化结构如下:
id user_id name description status created_at updated_at当用户访问某个知识库时,knowledge-service 要做两件事:
1. 查询知识库是否存在 2. 判断 knowledge_base.user_id 是否等于当前用户 ID如果不相等,就不能继续访问。
创建知识库
创建知识库时,前端只需要提交名称和描述。
用户 ID 不应该由前端传入,而应该来自当前登录用户。
流程如下:
前端提交 name、description -> Gateway 校验 Token -> knowledge-service 获取当前 userId -> 创建 knowledge_base -> knowledge_base.user_id = 当前 userId这样用户无法伪造 owner。
查询我的知识库
查询列表时,只返回当前用户自己的数据:
where user_id = 当前 userId管理员查看全局知识库则走 /admin/** 管理端接口,不和普通用户接口混用。
查看知识库详情
查看详情时,除了按 ID 查询,还要检查 owner。
错误思路:
select * from knowledge_base where id = kbId正确思路:
select * from knowledge_base where id = kbId and user_id = 当前用户ID重点是不能遗漏 owner 条件。
knowledge_base 表设计
user_id 是资源隔离的核心字段,必须出现在索引中。
CREATE TABLE knowledge_base ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '知识库ID', user_id BIGINT NOT NULL COMMENT '知识库owner用户ID', name VARCHAR(100) NOT NULL COMMENT '知识库名称', description VARCHAR(500) DEFAULT NULL COMMENT '知识库描述', status VARCHAR(32) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED/DISABLED/DELETED', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), KEY idx_kb_user_status_updated (user_id, status, updated_at), KEY idx_kb_user_name (user_id, name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库表';idx_kb_user_status_updated 用于“查询我的知识库”这类高频接口;idx_kb_user_name 可以辅助同一用户下的名称查询或名称查重。
04 Redis owner 缓存设计
知识库 owner 校验会被频繁使用。
比如:
- 查询知识库详情。
- 上传文档。
- 查询文档列表。
- 发起向量检索。
- 发起 RAG 问答。
如果每次都查 MySQL,压力虽然不一定很大,但链路会更长。KnowHub 使用 Redis 缓存知识库 owner,减少重复查询。
缓存 Key 设计
owner 缓存 key 可以设计为:
rag:kb:owner:{kbId}例如:
rag:kb:owner:100value 保存 owner 用户 ID:
2查询流程
校验知识库 owner 时,流程如下:
1. 根据 kbId 读取 Redis:rag:kb:owner:{kbId} 2. 如果命中,拿到 ownerUserId 3. 判断 ownerUserId 是否等于当前 userId 4. 如果未命中,查询 MySQL 5. MySQL 查到后写入 Redis 6. 再进行 owner 判断这样第一次访问会查数据库,后续访问可以直接命中 Redis。
缓存失效
缓存不是数据库,不能只写不删。
当知识库被删除、状态变化或 owner 发生变化时,需要删除对应缓存。
否则可能出现:
MySQL 中知识库已删除 Redis 中 owner 仍然存在 系统误以为资源还能访问缓存不是安全边界
这里要强调一点:Redis 缓存只是加速 owner 判断,不是权限本身。
真正的权限依据仍然是数据库中的资源归属。
如果缓存未命中,就回源 MySQL。如果缓存异常,也应该能回退到数据库校验,而不是直接放行。
05 文档、索引任务和问答中的隔离
知识库 owner 校验不是只在知识库接口里用。
它贯穿文档、索引任务和问答链路。
文档上传
用户上传文档时,必须先校验:
当前用户是否拥有这个 kbId只有校验通过,系统才会继续:
上传文件到 MinIO 写入 document_info 创建 index_task 发送 RabbitMQ 消息如果不做这个校验,用户就可能往别人的知识库上传文档。
文档列表
查询文档列表时,也要先校验知识库归属。
用户只能查看自己知识库下的文档。
向量检索
向量检索时,不能只按问题相似度查 TopK。
必须加上范围过滤:
user_id = 当前用户ID kb_id = 当前知识库ID否则可能出现严重问题:用户提问时,检索到了别人知识库里的片段。
这比普通接口越权更隐蔽,因为用户可能不会直接看到文档列表,却会在答案里看到不该看到的信息。
RAG 系统不能只相信相似度排序,必须先限制检索范围。
RAG 问答
问答链路也要先校验知识库归属。
流程大致是:
用户提问 -> 校验 kb owner -> 问题向量化 -> 按 userId 和 kbId 检索 -> 拼接 Prompt -> 调用模型 -> 写 qa_log -> 返回答案和引用来源qa_log 中也应该记录 userId 和 kbId,方便后续管理端查询和问题排查。
索引任务
索引任务也要带上 userId、kbId、documentId。
这样管理员能看到全局任务,普通用户只能看到自己文档对应的任务。
RabbitMQ 消息中也应该包含这些 ID,便于 task-service 消费时校验和更新状态。
06 基础后台管理端
KnowHub 不只有用户端,还有基础后台管理端。
管理端的目标不是做一个复杂企业后台,而是提供运维视角,让管理员能看见系统运行情况。
USER 和 ADMIN
当前基础角色包括:
USER ADMIN普通用户登录后只能访问用户端。
管理员登录后可以访问管理端。
角色信息会写入 JWT,Gateway 根据角色拦截 /admin/**。
管理端能做什么
基础管理端通常包含这些能力:
- 查看用户列表。
- 修改用户状态。
- 修改用户角色。
- 查看全部知识库。
- 查看全部文档。
- 查看索引任务。
- 筛选失败任务。
- 手动重试失败任务。
- 查看问答日志。
这些功能对排查问题很重要。
比如用户说“我的文档一直不能问答”,管理员可以查看:
文档是否上传成功 索引任务是否 SUCCESS 失败原因是什么 RabbitMQ 消息是否消费 MinIO 文件是否存在 qa_log 中是否有模型失败记录Gateway 管理员拦截
管理端接口必须走 /admin/**。
Gateway 看到这个路径后,会检查 Token 中的 role。
如果不是 ADMIN,直接拒绝。
这比只在前端隐藏菜单更可靠。
管理端前端不是安全边界
前端可以做角色判断,提高体验。
比如普通用户登录管理端时,前端提示:
当前账号不是管理员但真正的安全边界在后端。
因为用户可以绕过前端,直接调用接口。所以 /admin/** 必须由 Gateway 和后端接口共同保护。
07 为什么当前不是完整 RBAC
KnowHub 当前使用的是基础角色模型。
也就是:
USER ADMIN这已经能满足学习项目中的用户端和管理端隔离。
但它不是完整 RBAC。
完整 RBAC 通常还包括:
- 用户表。
- 角色表。
- 权限表。
- 用户角色关系表。
- 角色权限关系表。
- 菜单权限。
- 按钮权限。
- 数据权限。
- 操作审计。
比如一个完整企业后台可能有这些角色:
超级管理员 知识库管理员 审计员 普通用户 只读用户不同角色能访问不同菜单、不同接口、不同数据范围。
KnowHub 当前没有把 RBAC 做到这个复杂度,是有意取舍。
原因是本系列主线是 AI 知识库 / RAG 平台,不是权限系统教程。我们先用 USER / ADMIN 建立清晰边界,后续可以把完整 RBAC 作为企业化扩展。
08 常见问题排查
UserContext 为空
现象:访问知识库接口时提示未登录或用户上下文不存在。
排查顺序:
- 前端是否携带 Authorization。
- 请求是否经过 Gateway。
- Gateway 是否写入
X-User-Id。 - knowledge-service 是否配置 UserContextInterceptor。
- 请求头名称是否一致。
A 用户能看到 B 用户数据
这是严重问题。
排查顺序:
- 查询接口是否带了
user_id条件。 - 详情接口是否校验 owner。
- 文档接口是否先校验 kb owner。
- 向量检索是否按 userId/kbId 过滤。
- 管理端接口是否误暴露给普通用户。
Redis owner 缓存脏数据
现象:数据库中知识库已经删除或变更,但接口仍然认为它存在。
排查:
- 删除知识库时是否删除
rag:kb:owner:{kbId}。 - 缓存 TTL 是否过长。
- 是否有回源 MySQL 校验。
- Redis 中的 value 是否和数据库一致。
普通用户访问管理接口
普通用户访问 /admin/** 返回 403 是正常的。
如果普通用户能访问成功,要检查:
- Token 中 role 是否错误。
- Gateway 是否启用了 admin 路径校验。
- 路径是否没有以
/admin/开头。 - 后端管理接口是否绕过了 Gateway。
管理员登录后仍然无权限
排查顺序:
- 数据库中用户 role 是否为 ADMIN。
- 登录后新签发的 Token 是否包含 ADMIN。
- 前端是否仍使用旧 Token。
- Gateway 是否正确解析 role。
- role 大小写是否一致。
向量检索返回了疑似他人知识库的内容
这是 RAG 系统里很隐蔽也很严重的问题。
排查顺序:
- 向量检索 SQL 是否带了 userId 过滤条件。
- userId 是否从 UserContext 获取,而不是从请求参数获取。
- pgvector 向量记录是否包含 userId 字段。
- task-service 写入向量时是否正确设置 userId。
- 查询条件正确但仍异常时,再检查 pgvector 索引和查询执行计划。
09 动手验证:用户资源隔离是否生效
第一步,注册两个用户 A 和 B,分别登录获取各自的 Token。
POST http://localhost:8080/auth/register Body: {”username”:”userA”,”password”:”123456”,”nickname”:”用户A”}POST http://localhost:8080/auth/login Body: {”username”:”userA”,”password”:”123456”} 预期结果:返回用户 A 的 Token同样方式注册并登录用户 B。
第二步,用户 A 创建一个知识库,记下 kbId。
POST http://localhost:8080/kb Authorization: Bearer 用户A的Token Body: {”name”:”A的知识库”,”description”:”资源隔离验证”} 预期结果:创建成功,返回 kbId第三步,用户 B 访问用户 A 的知识库。
GET http://localhost:8080/kb/{用户A的kbId} Authorization: Bearer 用户B的Token 预期结果:返回“知识库不存在”或类似错误第四步,用户 B 往用户 A 的知识库上传文件。
POST http://localhost:8080/kb/{用户A的kbId}/documents/upload Authorization: Bearer 用户B的Token Body: form-data,file=任意 txt/pdf 文件 预期结果:返回错误,不能上传成功第五步,用户 A 访问自己的知识库。
GET http://localhost:8080/kb/{用户A的kbId} Authorization: Bearer 用户A的Token 预期结果:正常返回知识库详情第六步,普通用户 A 访问管理端索引任务。
GET http://localhost:8080/admin/index-tasks Authorization: Bearer 用户A的Token 预期结果:返回 403如果某一步不符合预期,先确认 Gateway 是否正常透传了 X-User-Id,再确认当前登录用户的 userId 是否和知识库的 user_id 对应,然后逐步排查 Service 查询条件、Redis owner 缓存、MySQL 数据和 Gateway 管理员路径校验。
本章小结
这一章我们讲清楚了 KnowHub 的用户资源隔离和基础后台管理设计。
登录认证只能证明用户是谁,不能代表用户能访问所有资源。真正的资源边界必须在业务服务中校验。
知识库是 KnowHub 的核心资源,因此 knowledge_base.user_id 是用户隔离的基础。用户创建知识库时,owner 来自当前登录用户;用户查询、上传文档、发起问答时,都必须校验知识库归属。
为了减少高频 owner 校验带来的数据库访问,系统使用 Redis 缓存 rag:kb:owner:{kbId},但缓存只是加速手段,不能替代数据库中的权限依据。
管理端基于 USER / ADMIN 做基础角色隔离。Gateway 负责拦截 /admin/**,管理端接口负责提供用户、知识库、文档、索引任务和问答日志的运维视角。
下一章,我们将进入知识库管理与文档元数据,开始讲用户真正操作的第一个业务核心:创建知识库、上传文档,以及如何把文件记录成可追踪的数据。
作者有话说
如果这篇文章对你有帮助,欢迎点个关注。
这个专栏会持续更新KnowHub / RAG 平台实战内容,后面会继续把知识库管理、文档元数据、文件存储、文档解析和索引任务拆开讲清楚。
如果你想对照代码学习,可以结合下面两个仓库:
rag-demo-monolith:单体版源码
适合先理解 RAG 核心闭环,把业务链路跑通。
仓库地址:
https://gitee.com/MrLuoBin/rag-demo-monolith.githttps://github.com/luobinsnbb/rag-demo-monolith.git克隆命令:
git clone https://gitee.com/MrLuoBin/rag-demo-monolith.gitrag-platform:微服务版源码
适合继续学习 Gateway、Auth、Knowledge、Task 的企业级拆分方式。
仓库地址:
https://gitee.com/MrLuoBin/rag-platform.git克隆命令:
git clone https://gitee.com/MrLuoBin/rag-platform.git如果你在做 AI 知识库或 RAG 项目,建议把“用户能访问什么资源”作为第一优先级设计。RAG 系统一旦发生检索越权,风险比普通接口越权更隐蔽。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~