08. Persistence 与 Security:状态、密钥和权限
8.1 数据库保存的是平台状态
packages/@n8n/db/src/entities 可以看到 n8n 的平台模型:workflow-entity、execution-entity、execution-data、credentials-entity、user、project、shared-workflow、shared-credentials、webhook-entity 等。
工作流版本、发布状态、execution 历史和项目共享关系分别回答不同问题:当前编辑内容是什么、生产版本是什么、某次运行发生了什么、谁可以访问。不要把 workflow JSON 当成唯一事实来源。
8.2 Execution 数据和二进制数据
执行结果包含 JSON item,也可能包含图片、文件、音视频等 binary。二进制数据有独立的 binary-data 管理路径,可以落在文件系统、S3、Azure Blob 等存储。这样数据库不必承担所有大对象,但 execution JSON 中仍会保留 binary id/metadata。
需要关注:
packages/cli/src/executions/execution.service.tspackages/cli/src/binary-data/database.manager.tspackages/@n8n/blob-storage- 根
docs/db.md和@n8n/db的连接/迁移代码
8.3 凭证加密和解析时机
凭证至少有三个时机:
保存:明文表单 → credential type 校验 → 加密字符串入库
执行:密文入库 → 解密 → 解析表达式/动态 token → 请求认证
展示:只返回脱敏 metadata,不把 secret 回送编辑器
CredentialsHelper 负责把 credential type 的认证声明转成实际 HTTP options。带过期 token 的 credential 可能在执行前 preAuthentication,并将刷新后的 token 写回,但源码特别保护“用户保存的是表达式”这一情况,避免把本次解析出的静态 token 覆盖掉动态表达式。
8.4 认证和浏览器会话
packages/cli/src/auth/auth.service.ts 的 JWT payload 不只包含 user id,还带密码派生 hash、browser id、MFA 使用状态和 embed 标记。中间件会检查:
- cookie token 是否被 invalidated。
- JWT 是否有效、用户是否存在。
- MFA 是否已使用或需要 enrollment。
- browser id 是否匹配,哪些 endpoint 必须豁免。
- preview/unauthenticated endpoint 是否允许继续。
这不是单纯的“有 token 就放行”,而是把会话、浏览器来源和 MFA 状态合并成请求身份。
8.5 Project scope
权限不是只有 global role。ProjectScopeService 的关键逻辑是:如果用户的全局角色具备 scope,返回 null 表示可以访问所有项目;否则根据角色和 ProjectRelationRepository 查询可访问项目 id,再把范围注入查询。
request user + required scopes
→ global scope? → all projects
→ otherwise role scopes
→ project relations
→ repository query constrained by project ids
这解释了为什么 controller 能通过 scope 装饰器表达权限,而 service/repository 仍需要显式 project filter:权限校验和数据范围是两层防线。
8.6 设计取舍
执行状态和 workflow 分离支持历史查询、清理和重放,但要处理 schema 迁移、execution pruning 和版本对应。
密钥只在 helper 解密减少泄漏面,但任意节点执行上下文一旦允许读取 credentials,就必须严格审查日志、错误序列化和表达式访问。
项目级 scope比全局角色细粒度,适合团队协作;代价是每个查询、缓存和后台任务都必须带上正确的 project context。