# 安全纵深防御实施计划 > 设计规格: `docs/superpowers/specs/2026-04-26-security-defense-in-depth-design.md` > 日期: 2026-04-26 | 状态: draft | 总周期: 2-3 周 --- ## Phase 1: PostgreSQL RLS 安全网(Week 1) ### Task 1: 创建 RLS 策略迁移 **涉及文件**: `crates/erp-server/migration/src/m000073_enable_rls_all_tables.rs`(新增), `lib.rs` **步骤**: 对所有含 tenant_id 的表(30 基础 + 34 健康)执行 `ALTER TABLE ENABLE ROW LEVEL SECURITY` + `CREATE POLICY tenant_isolation USING (tenant_id = current_setting('app.current_tenant_id')::uuid)` + `CREATE POLICY tenant_bypass USING (current_user IN ('erp_admin', 'erp_migration'))`。创建 `erp_app` 数据库角色。down 方法完整回退。 **验收**: 迁移执行成功;以 erp_app 角色未设置 tenant_id 时查询返回空;现有应用行为不变。 ### Task 2: Axum middleware 设置当前连接的 tenant_id **涉及文件**: `crates/erp-server/src/middleware/tenant.rs` **步骤**: tenant 中间件从 JWT 解析 tenant_id 后执行 `SET LOCAL app.current_tenant_id = ''`。确认在事务内执行(SeaORM 显式事务包装或 session 级 SET + RESET)。添加 tracing 日志记录注入状态。SET LOCAL 失败时 warn 但不阻断请求。 **验收**: 注入后以 erp_app 角色查询自动按 tenant_id 过滤;`cargo test --workspace` 通过。 ### Task 3: 验证现有测试不受影响 **涉及文件**: 可能修改测试辅助代码 **步骤**: 运行 `cargo test --workspace` 检查 RLS 导致的测试失败。分析失败原因(测试未设置 tenant_id → 查询返回空),在 TestDb/TestApp 初始化时注入 tenant_id。不修改任何业务逻辑代码。 **验收**: `cargo test --workspace` 全部通过。 --- ## Phase 2: 行级数据范围 + session_key Redis(Week 2) ### Task 4: require_permission 增加 data_scope 过滤逻辑 **涉及文件**: `erp-core/src/types.rs`(TenantContext 增加 permission_data_scopes), `erp-core/src/rbac.rs`(apply_data_scope 函数), JWT 中间件, `erp-health/src/handler/mod.rs` **步骤**: TenantContext 新增 `permission_data_scopes: HashMap`(枚举 All/Self/Department/DepartmentTree)。JWT 中间件查询 `role_permissions.data_scope` 填充。实现 `apply_data_scope(query, ctx, permission, owner_column, dept_column)` 按变体追加 filter。各 health handler 列表查询调用此函数。 **验收**: data_scope = Department 时查询自动追加部门过滤;未配置时默认 All 向后兼容。 ### Task 5: 微信 session_key 从 HashMap 迁移到 Redis **涉及文件**: `crates/erp-auth/src/service/wechat_service.rs`, `erp-server/src/app_state.rs` **步骤**: 替换 `LazyLock>` 为 Redis `SET wechat:session:{openid} {key} EX 300` / `GET + DEL`。Redis 连接池通过 AuthState 传入。添加 fallback:Redis 不可用时降级内存 HashMap 并 warn 日志。 **验收**: 小程序登录端到端通过;`cargo test -p erp-auth` 通过。 ### Task 6: 小程序 openid 加密存储 **涉及文件**: `apps/miniprogram/src/utils/secure-storage.ts`(新增), `stores/auth.ts` **步骤**: 创建 AES 加密存储工具(setSecure/getSecure)。后端登录接口响应新增 `storage_key` 字段。auth.ts 中 openid 存储改用 `setSecure('openid', openid)`。 **验收**: `Taro.getStorageSync('openid')` 返回密文;小程序登录端到端通过。 --- ## Phase 3: 健康检查增强 + 审计日志(Week 2-3) ### Task 7: /health/ready 增加 DB ping + Redis ping **涉及文件**: `crates/erp-server/src/handlers/health.rs` **步骤**: 添加 `sqlx::query("SELECT 1")` DB 检查 + `redis PING` 检查,使用 `tokio::join!` 并行。响应扩展 status(ok/degraded) + database + redis 字段。 **验收**: DB 不可用时返回 `status: "degraded"` + `database: "unreachable"`。 ### Task 8: audit_logs 表增加 prev_hash 字段实现哈希链 **涉及文件**: `migration/src/m000074_audit_logs_hash_chain.rs`(新增), `erp-core/src/audit.rs` **步骤**: 迁移添加 `prev_hash TEXT` + `record_hash TEXT` 列。写入时查询最新 record_hash 作为 prev_hash,计算 `SHA256(id + action + resource_type + resource_id + created_at + prev_hash)` 作为 record_hash。添加完整性验证函数检测篡改。内存缓存最近 1000 条 record_hash 优化性能。 **验收**: 新审计日志含哈希链字段;修改记录后验证函数检测到链断裂;`cargo test` 通过。 --- ## 执行原则 1. **每 Task 完成后立即提交** — 不积压 2. **Phase 1 最高优先** — RLS 是医疗数据合规红线 3. **RLS 迁移必须可回退** — down 方法完整恢复 4. **渐进式启用 data_scope** — 未配置默认 All,不破坏现有行为