feat(security): add distributed authorization foundation
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# 认证与授权待补强清单
|
||||
|
||||
> 2026-07-29 实施状态:可信代理启动校验、外部登录成员生命周期、Redis 跨实例频控与故障关闭、数据库 Capability、事务化 System Scope 审计、MassTransit EF Bus/Consumer Outbox、浏览器 Cookie/CSRF 主链路及 endpoint manifest 已落地。生产网关 ACL、RabbitMQ 4.x 重启/积压演练和按 job type 迁移旧轮询 Worker 仍属于部署验收项。
|
||||
|
||||
当前生效规则见 [认证、授权与 Host 安全策略](authentication-authorization-security.md)。本文只记录尚需补强的安全事项,不重复描述已实现体系。
|
||||
|
||||
## P0:可信代理与 Host fail-closed
|
||||
@@ -10,6 +12,8 @@
|
||||
- 受信代理只接受一跳转发,网关必须覆盖客户端伪造的 Forwarded Headers。
|
||||
- API 公网入口必须只能由受信网关访问。
|
||||
|
||||
实现说明:应用已强制 `ForwardLimit=1`,Production 缺少正式 Host、显式 `AllowedHosts` 或可信代理地址时启动失败;公网 ACL 和网关覆盖转发头由部署层落实。
|
||||
|
||||
验收:
|
||||
|
||||
- Host A + Tenant B token 返回 403。
|
||||
@@ -23,6 +27,8 @@
|
||||
- 首次外部登录是否允许创建学生成员,必须由租户自注册策略控制。
|
||||
- 成员恢复只能由管理员显式操作并写审计。
|
||||
|
||||
实现说明:`TenantAuthPolicy.AllowExternalStudentSelfRegistration` 控制首次外部登录;Disabled/Invited 不会被登录激活,管理员成员变更会同步撤销 Session、写审计并发布生命周期事件。
|
||||
|
||||
验收:
|
||||
|
||||
- Disabled 成员旧 access/refresh 立即失效。
|
||||
@@ -43,6 +49,8 @@ Tenant Active
|
||||
+ DataScope / Resource Scope
|
||||
```
|
||||
|
||||
实现说明:`ProductModule`、`PlanModuleEntitlement`、`TenantModuleOverride` 与 `ICapabilityAccessEvaluator` 已进入数据库授权 Handler。模块目录采用渐进启用:只有进入固定目录的模块才强制套餐校验,避免迁移时误封未建档模块。
|
||||
|
||||
验收:
|
||||
|
||||
- 有 permission 但套餐不含模块,返回 403。
|
||||
@@ -59,6 +67,8 @@ Tenant Active
|
||||
- All-only 资源必须显式声明。
|
||||
- 新增 Controller action 未进入 manifest 时测试失败。
|
||||
|
||||
实现说明:`AuthorizationManifestTests` 对全部 Controller HTTP Action 的 method、route、匿名标记和 policy 生成稳定摘要;MVC convention 同时为全部非匿名 Controller endpoint 生成 realm、module、permission、operation、All-only 与 audit action 运行时元数据,变更会触发测试失败并要求安全评审。
|
||||
|
||||
验收:
|
||||
|
||||
- 列表、详情、创建、更新、删除、批量、导出和 Worker job 使用一致 DataScope。
|
||||
@@ -70,6 +80,8 @@ Tenant Active
|
||||
- 平台操作、Worker、迁移验证和受审计公共题库服务才允许使用 System Scope。
|
||||
- 跨租户写操作必须落 `AuditLog`。
|
||||
|
||||
实现说明:`SystemScopeRequest` 强制 caller、reason、target tenant 和 correlation ID;旧参数签名已移除。成功路径的 entered 审计、跨租户业务写入和 completed 审计处于同一 PostgreSQL 事务,异常路径回滚业务并持久化 entered/failed 审计。
|
||||
|
||||
验收:
|
||||
|
||||
- 未声明 reason 的 System Scope 创建失败。
|
||||
@@ -82,3 +94,5 @@ Tenant Active
|
||||
- Access token 继续短期有效,不把角色和权限写入 JWT。
|
||||
- 登录审计和错误响应避免泄露手机号、openId、邮箱完整值。
|
||||
- 出现第三方生态登录、开放 API 或多客户端授权需求时,再评估 OpenIddict / OIDC,不继续扩展私有协议。
|
||||
|
||||
实现说明:浏览器使用 `/api/browser-auth` + Secure/HttpOnly Cookie + Origin/CSRF 校验;原 `/api/auth` Bearer 契约继续供小程序、原生和服务调用。
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
- Host、JWT scope、tenant claim、数据库 Session 和请求租户上下文必须一致。
|
||||
- JWT 只证明已认证会话,不承载可直接授权的角色或权限。
|
||||
- 后台权限每次从数据库角色绑定解析;菜单只控制 UI 展示。
|
||||
- 租户后台能力同时要求 Active tenant、有效订阅、模块权益和 operation permission;Capability 仍以 PostgreSQL 为准。
|
||||
- 数据权限必须进入 SQL;无法可靠映射 owner、region 或 class 的资源采用 All-only fail-closed。
|
||||
- 用户、成员、租户、后台角色、权限、SecurityStamp 或 Session 任一失效,旧 token 不能继续取得能力。
|
||||
- Controller 默认要求认证;公开接口必须显式 `[AllowAnonymous]`。
|
||||
@@ -54,6 +55,8 @@ v2.{t|p}.{tenantId|-}.{sessionId}.{64-byte-random-secret}
|
||||
- 已轮换 token 被复用时视为重放,撤销整个 token family 并写审计。
|
||||
- logout 撤销当前 refresh token family;logout-all 更新 SecurityStamp 并撤销用户全部 Session。
|
||||
|
||||
浏览器入口使用 `/api/browser-auth/*`:access/refresh token 仅写入 Secure、HttpOnly Cookie,响应体不返回 token;不安全方法必须通过同源 Origin 与双提交 CSRF 校验。`/api/auth/*` Bearer 契约继续供小程序、原生客户端和服务调用。
|
||||
|
||||
## Host 与 tenant 解析
|
||||
|
||||
Host 是认证上下文,不是普通参数。`TenantResolutionMiddleware` 在 Authentication 前执行。
|
||||
@@ -83,6 +86,8 @@ Host 是认证上下文,不是普通参数。`TenantResolutionMiddleware` 在
|
||||
- 平台角色不带租户键,不能自动读取租户业务数据。
|
||||
- 菜单只决定 UI bootstrap 展示,不作为 API 授权依据。
|
||||
- 后台 API 必须声明明确 permission;高风险写操作必须记录审计。
|
||||
- UI bootstrap 只返回“有效 permission 推导菜单”与有效 Capability 的交集;租户不能绑定当前无权使用的模块权限。
|
||||
- Trial/Active 且在有效期内可写;PastDue/Cancelled/Expired 仅允许已有权益模块的历史读取。
|
||||
|
||||
DataScope:
|
||||
|
||||
@@ -97,6 +102,15 @@ DataScope:
|
||||
- `ISmsProvider` 只负责发送。
|
||||
- 发送失败必须记录失败状态,不能留下可验证验证码。
|
||||
- 登录、绑定、找回密码等场景使用独立 purpose 和频控键。
|
||||
- Redis Lua 同时执行跨实例 IP、账号、租户、手机号和 purpose 窗口计数;key 只使用 GUID 或不可逆哈希。
|
||||
- Redis 不可用时密码尝试、短信发送和短信校验失败关闭;普通授权请求仍直接查询 PostgreSQL。
|
||||
|
||||
## 可靠安全事件
|
||||
|
||||
- `Tiku.Contracts` 只包含版本化 DTO,不引用 EF、HTTP 或 Provider SDK。
|
||||
- API 使用 MassTransit EF Bus Outbox,Worker consumer 使用 EF inbox/outbox;业务变更、审计和消息由同一 DbContext 提交。
|
||||
- RabbitMQ 消息只负责非权威失效版本、菜单刷新和下游通知;成员、租户、Session 或套餐失效不等待 consumer。
|
||||
- System Scope 只能通过完整 `SystemScopeRequest` 创建;成功路径将 entered 审计、跨租户业务写入和 completed 审计放入同一 PostgreSQL 事务。
|
||||
|
||||
## 审计与错误
|
||||
|
||||
@@ -121,6 +135,9 @@ DataScope:
|
||||
- JWT issuer、audience、当前 `KeyId`、RSA 私钥和旧公钥集合。
|
||||
- Data Protection 证书。
|
||||
- CORS 明确 Origin。
|
||||
- Redis 7.2+ 连接串;Production 缺失时拒绝启动。
|
||||
- RabbitMQ 4.x Host、virtual host 与凭据;Production 缺失时拒绝启动。
|
||||
- 公网只暴露覆盖 Forwarded Headers 的可信网关,API ACL 只允许该网关访问。
|
||||
- Secret encryption key。
|
||||
- 短信、对象存储、支付、通知和 AI provider 只通过租户 Provider 配置读取密钥。
|
||||
|
||||
|
||||
9
docs/architecture/endpoint-authorization-manifest.md
Normal file
9
docs/architecture/endpoint-authorization-manifest.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Endpoint authorization manifest
|
||||
|
||||
Controller 授权面由 `AuthorizationManifestTests` 按 HTTP method、route、controller/action、匿名标记和 policy 生成稳定摘要。
|
||||
`EndpointAuthorizationMetadataConvention` 为全部非匿名 Controller endpoint 生成 realm、module、permission、CapabilityOperation、All-only DataScope 与 audit action 元数据,测试从运行时 `EndpointDataSource` 验证覆盖。新增、删除或修改 Action 时摘要测试必须失败,评审者确认元数据后才能更新 count/hash。
|
||||
|
||||
该清单是防止接口绕过评审的变更门禁;实际授权事实仍来自 PostgreSQL permission、Capability 和 DataScope,不能用摘要替代运行时校验。
|
||||
|
||||
- Action 数量:330
|
||||
- SHA-256:`ad09167662cb9dc25111f40902c5f16a6633465f0ca7e7da0e50cdc10cfb8bb5`
|
||||
@@ -1,5 +1,18 @@
|
||||
# 本地开发快速开始
|
||||
|
||||
## 可选分布式依赖
|
||||
|
||||
本地单实例开发可以不配置 Redis/RabbitMQ,认证频控仍保留 PostgreSQL/进程内防线;Production 两者均为启动必填项。
|
||||
|
||||
```bash
|
||||
export ConnectionStrings__Redis='localhost:6379,abortConnect=false'
|
||||
export RabbitMq__Host='rabbitmq://localhost'
|
||||
export RabbitMq__Username='guest'
|
||||
export RabbitMq__Password='guest'
|
||||
```
|
||||
|
||||
RabbitMQ 使用 MassTransit 8.5.10 和 PostgreSQL EF Bus/Consumer Outbox。`GET /api/health` 是 liveness,`GET /api/health/ready` 检查 PostgreSQL、已配置的 Redis、RabbitMQ bus health 和 outbox backlog;服务健康不等于认证授权验收完成。
|
||||
|
||||
这份文档用于从全新开发环境启动 TIKU Backend、初始化 PostgreSQL,并完成平台管理员的首次登录。
|
||||
|
||||
## 1. 准备环境
|
||||
|
||||
Reference in New Issue
Block a user