513 lines
14 KiB
Markdown
513 lines
14 KiB
Markdown
# IAM Service — Scalar 调用顺序指南(v0.1.0)
|
||
|
||
## Authentication(认证方式)
|
||
|
||
本服务使用 **JWT Bearer Token**:
|
||
|
||
- 登录成功后拿到 `access_token`
|
||
- 后续请求在 Header 中带:
|
||
- `Authorization: Bearer <access_token>`
|
||
- 租户上下文:
|
||
- 保护接口默认从 Token claim 的 `tenant_id` 推导租户
|
||
- 可选兼容 `X-Tenant-ID: <uuid>`,若同时提供 Header 与 Token,则必须一致,否则返回 403
|
||
|
||
访问令牌(JWT)除 `tenant_id/user_id/roles/permissions` 外,还包含:
|
||
|
||
- `apps`:租户已开通应用列表(如 `["cms","tms"]`)
|
||
- `apps_version`:租户 enabled_apps 版本号(用于客户端判断是否需要刷新会话)
|
||
|
||
## OpenAPI/Scalar 可配置参数(环境变量)
|
||
|
||
为方便本地调试与演示,服务会在生成 OpenAPI 文档时,将部分 Header 参数的示例值按环境变量动态注入(影响 `/scalar` 展示,不影响服务鉴权逻辑):
|
||
|
||
- `IAM_DOCS_DEFAULT_TENANT_ID`:默认租户 ID(用于 `X-Tenant-ID` Header 的 example)
|
||
- 未设置时默认:`11111111-1111-1111-1111-111111111111`
|
||
- 可选强制:`IAM_DOCS_REQUIRE_TENANT_ID=1`(未设置则启动时直接失败)
|
||
- `IAM_DOCS_DEFAULT_TOKEN`:默认 Token(用于 `Authorization` Header 的 example)
|
||
- 可填写裸 JWT 或 `Bearer <jwt>`;裸 JWT 会自动补齐 `Bearer `
|
||
- 未设置时默认:`Bearer <access_token>`
|
||
- 可选强制:`IAM_DOCS_REQUIRE_TOKEN=1`(未设置则启动时直接失败)
|
||
|
||
## 通用响应结构
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{ "code": 0, "message": "Success|Created|Accepted", "data": {}, "trace_id": null }
|
||
```
|
||
|
||
错误响应(示例):
|
||
|
||
```json
|
||
{ "code": 20006, "message": "Missing authorization header", "details": null, "trace_id": null }
|
||
```
|
||
|
||
常见错误码(节选):
|
||
|
||
- 20006:缺少必要 Header(如 Authorization)
|
||
- 20003:无权限(403)
|
||
- 20005:账号或密码错误
|
||
- 30000:请求参数错误(400)
|
||
- 30002:资源不存在(404)
|
||
- 30003:资源冲突(409)
|
||
- 40000:请求过于频繁(429)
|
||
- 10001:数据库错误(500)
|
||
|
||
## Step-by-step(可复制流程)
|
||
|
||
### Step -1:数据库初始化 / 迁移(开发/测试/生产)
|
||
|
||
本服务新增了“租户已开通应用(enabled_apps)”与“平台超级管理员(SuperAdmin)”能力,对应数据库新增表:
|
||
|
||
- `apps`
|
||
- `tenant_entitlements`
|
||
- `tenant_enabled_apps_history`
|
||
|
||
推荐使用版本化迁移脚本初始化与升级:
|
||
|
||
- 执行迁移:`scripts/db/migrate.sh`
|
||
- 执行校验:`scripts/db/verify.sh`
|
||
- 回滚(按版本):`scripts/db/rollback.sh`
|
||
|
||
本仓库同时保留开发用的一键重建脚本:`scripts/db/rebuild_iam_db.sh`(会清库重建,不适合生产)。
|
||
|
||
### Step 0:创建租户(可选)
|
||
|
||
**POST** `/tenants/register`
|
||
|
||
- Tag:`Tenant`
|
||
- Header:无
|
||
- 二次验证(可选但建议生产启用):
|
||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,则必须传 Header:`X-Sensitive-Token: <token>`,否则返回 403。
|
||
- Body:
|
||
|
||
```json
|
||
{ "name": "Tenant A", "config": { "theme": { "primary": "#1d4ed8" } } }
|
||
```
|
||
|
||
成功(201)从 `data.id` 取出租户 ID:
|
||
|
||
```json
|
||
{ "code": 0, "message": "Created", "data": { "id": "<tenant_id>", "name": "Tenant A", "status": "active", "config": {} }, "trace_id": null }
|
||
```
|
||
|
||
下一步依赖:`tenant_id`(用于注册/登录时的 `X-Tenant-ID`)。
|
||
|
||
### Step 0.1:平台超级管理员(可选,但推荐先完成)
|
||
|
||
数据库已内置平台租户(Platform tenant):
|
||
|
||
- `tenant_id = 00000000-0000-0000-0000-000000000001`
|
||
|
||
在平台租户下注册首个用户,将自动获得平台级权限(用于管理各租户 enabled_apps):
|
||
|
||
**POST** `/auth/register`
|
||
|
||
- Tag:`Auth`
|
||
- Header:`X-Tenant-ID: 00000000-0000-0000-0000-000000000001`
|
||
- Body:
|
||
|
||
```json
|
||
{ "email": "superadmin@example.com", "password": "securePassword123" }
|
||
```
|
||
|
||
### Step 1:注册用户
|
||
|
||
**POST** `/auth/register`
|
||
|
||
- Tag:`Auth`
|
||
- 必需 Header:`X-Tenant-ID: <tenant_id>`
|
||
- Body:
|
||
|
||
```json
|
||
{ "email": "user@example.com", "password": "securePassword123" }
|
||
```
|
||
|
||
成功(201)从 `data.id` 取出 `user_id`(后续可用于用户管理接口):
|
||
|
||
```json
|
||
{ "code": 0, "message": "Created", "data": { "id": "<user_id>", "email": "user@example.com" }, "trace_id": null }
|
||
```
|
||
|
||
### Step 2:登录获取访问令牌(Authentication 入口)
|
||
|
||
**POST** `/auth/login`
|
||
|
||
- Tag:`Auth`
|
||
- 必需 Header:`X-Tenant-ID: <tenant_id>`
|
||
- Body:
|
||
|
||
```json
|
||
{ "email": "user@example.com", "password": "securePassword123" }
|
||
```
|
||
|
||
成功(200)从 `data.access_token` 取出访问令牌:
|
||
|
||
```json
|
||
{ "code": 0, "message": "Success", "data": { "access_token": "<jwt>", "refresh_token": "<opaque>", "token_type": "Bearer", "expires_in": 900 }, "trace_id": null }
|
||
```
|
||
|
||
下一步依赖:`access_token`。
|
||
|
||
(可选)使用 refresh_token 自动续期:
|
||
|
||
- **POST** `/auth/refresh`
|
||
- Tag:`Auth`
|
||
- Header:无
|
||
- Body:
|
||
|
||
```json
|
||
{ "refresh_token": "<opaque>" }
|
||
```
|
||
|
||
### Step 3:获取当前租户信息(Tenant)
|
||
|
||
**GET** `/tenants/me`
|
||
|
||
- Tag:`Tenant`
|
||
- 必需 Header:`Authorization: Bearer <access_token>`
|
||
- 可选 Header:`X-Tenant-ID: <tenant_id>`(如提供必须与 token tenant_id 一致)
|
||
|
||
成功(200):
|
||
|
||
```json
|
||
{ "code": 0, "message": "Success", "data": { "id": "<tenant_id>", "name": "Tenant A", "status": "active", "config": {} }, "trace_id": null }
|
||
```
|
||
|
||
### Step 4:查看当前用户权限(Me)
|
||
|
||
**GET** `/me/permissions`
|
||
|
||
- Tag:`Me`
|
||
- 必需 Header:`Authorization: Bearer <access_token>`
|
||
|
||
成功(200):
|
||
|
||
```json
|
||
{ "code": 0, "message": "Success", "data": ["tenant:read","tenant:write"], "trace_id": null }
|
||
```
|
||
|
||
下一步依赖:确认具备目标权限(例如 `user:read` / `role:read`)。
|
||
|
||
### Step 4.1:平台层设置租户已开通应用(SuperAdmin)
|
||
|
||
该能力仅允许拥有平台级权限的用户调用:
|
||
|
||
- `iam:tenant:enabled_apps:read`
|
||
- `iam:tenant:enabled_apps:write`
|
||
|
||
#### 4.1.1 查询某租户 enabled_apps
|
||
|
||
**GET** `/platform/tenants/{tenant_id}/enabled-apps`
|
||
|
||
- Tag:`Tenant`
|
||
- Header:`Authorization: Bearer <access_token>`(平台租户下登录得到的 token)
|
||
|
||
#### 4.1.2 设置某租户 enabled_apps(全量覆盖,幂等)
|
||
|
||
**PUT** `/platform/tenants/{tenant_id}/enabled-apps`
|
||
|
||
- Tag:`Tenant`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- Body:
|
||
|
||
```json
|
||
{ "enabled_apps": ["cms", "tms"], "expected_version": 0 }
|
||
```
|
||
|
||
说明:
|
||
- `expected_version` 可选,用于并发控制;不匹配会返回 409。
|
||
- 登录签发 token 时会自动把 `apps/apps_version` 注入到 JWT,并对 `permissions` 按 enabled_apps 过滤。
|
||
- `enabled_apps` 必须是“应用注册表 apps 表”中存在且 `status=active` 的应用 ID;若传入未知/已下线应用(例如 `dms`),接口会返回 400。
|
||
|
||
enabled_apps 维护建议:
|
||
- 新增应用:通过数据库迁移向 `apps(id,name,description,status)` 插入一行(`id` 推荐全小写短标识,如 `cms` / `tms`)。
|
||
- 下线应用:将 `apps.status` 置为非 `active`(例如 `disabled`),之后将无法再被设置进任何租户的 enabled_apps。
|
||
- 查询可用应用(示例 SQL):`SELECT id, name, status FROM apps ORDER BY id;`
|
||
|
||
### Step 4.2:平台层 App 生命周期管理(SuperAdmin)
|
||
|
||
用于维护“允许的 App 注册表”,并提供应用上下线(审批 + 生效时间)能力。
|
||
|
||
权限要求(平台级):
|
||
- `iam:app:read`
|
||
- `iam:app:write`
|
||
- `iam:app:approve`
|
||
- `iam:app:delete`
|
||
|
||
#### 4.2.1 新增 App
|
||
|
||
**POST** `/platform/apps`
|
||
|
||
- Tag:`App`
|
||
- Header:`Authorization: Bearer <access_token>`(平台租户下登录得到的 token)
|
||
- Body(示例):
|
||
|
||
```json
|
||
{ "id": "dms", "name": "DMS", "description": "Document Management System", "app_type": "product", "owner": "team-a" }
|
||
```
|
||
|
||
#### 4.2.2 查询 App 列表(分页/筛选/排序)
|
||
|
||
**GET** `/platform/apps?page=1&page_size=20&status=active&app_type=product&sort_by=created_at&sort_order=desc`
|
||
|
||
- Tag:`App`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
|
||
#### 4.2.3 更新 App 基础信息
|
||
|
||
**PATCH** `/platform/apps/{app_id}`
|
||
|
||
- Tag:`App`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- Body(示例):
|
||
|
||
```json
|
||
{ "description": "DMS v2", "owner": "team-b" }
|
||
```
|
||
|
||
#### 4.2.4 申请 App 上下线(需要审批,可设置生效时间)
|
||
|
||
**POST** `/platform/apps/{app_id}/status-change-requests`
|
||
|
||
- Tag:`App`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- Body(示例:立即禁用):
|
||
|
||
```json
|
||
{ "to_status": "disabled", "reason": "security patch" }
|
||
```
|
||
|
||
- Body(示例:延迟生效):
|
||
|
||
```json
|
||
{ "to_status": "disabled", "effective_at": "2026-02-01T00:00:00Z", "reason": "maintenance window" }
|
||
```
|
||
|
||
#### 4.2.5 审批上下线申请单
|
||
|
||
**GET** `/platform/app-status-change-requests?status=pending&page=1&page_size=20`
|
||
|
||
**POST** `/platform/app-status-change-requests/{request_id}/approve`
|
||
|
||
**POST** `/platform/app-status-change-requests/{request_id}/reject?reason=...`
|
||
|
||
#### 4.2.6 删除 App(软删除)
|
||
|
||
**DELETE** `/platform/apps/{app_id}`
|
||
|
||
- Tag:`App`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- 二次验证(可选但建议生产启用):
|
||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,则必须同时传 Header:`X-Sensitive-Token: <token>`,否则返回 403。
|
||
|
||
### Step 5:列出用户(User)
|
||
|
||
**GET** `/users?page=1&page_size=20`
|
||
|
||
- Tag:`User`
|
||
- 必需 Header:`Authorization: Bearer <access_token>`
|
||
- 分页规则:
|
||
- `page` 默认 1,必须 >= 1
|
||
- `page_size` 默认 20,范围 1..=200
|
||
|
||
成功(200):
|
||
|
||
```json
|
||
{ "code": 0, "message": "Success", "data": [{ "id": "<user_id>", "email": "user@example.com" }], "trace_id": null }
|
||
```
|
||
|
||
### Step 6:列出角色(Role)
|
||
|
||
**GET** `/roles`
|
||
|
||
- Tag:`Role`
|
||
- 必需 Header:`Authorization: Bearer <access_token>`
|
||
|
||
成功(200):
|
||
|
||
```json
|
||
{ "code": 0, "message": "Success", "data": [{ "id": "<role_id>", "name": "Admin", "description": "..." }], "trace_id": null }
|
||
```
|
||
|
||
## Role & Permission Management
|
||
|
||
本节补齐“角色(Role)与权限(Permission)管理”的完整指引,适用于横向扩展到多个应用(如 `cms` / `tms` / `iam` 等)。
|
||
|
||
### 权限模型(可扩展到十几个应用)
|
||
|
||
权限编码规范:
|
||
- `permission.code` 统一采用:`${app_code}:${resource}:${action}`
|
||
- 示例:`cms:article:publish`、`tms:task:assign`、`iam:tenant:enabled_apps:write`
|
||
- `app_code` 必须与应用注册表(app_registry)中的应用标识一致(本项目对应表为 `apps.id`)。
|
||
- 支持通配符(需要在 `permissions.code` 中显式存储通配符权限并分配到角色):
|
||
- `cms:*:*`:CMS 全权限
|
||
- `cms:article:*`:文章相关全权限
|
||
|
||
> 说明:鉴权时支持通配符匹配(例如持有 `cms:article:*` 可满足 `cms:article:publish`),但只有当该通配符权限被写入 `permissions` 并绑定到角色时才会生效。
|
||
|
||
数据库关系(ER 图):
|
||
|
||
```mermaid
|
||
erDiagram
|
||
apps {
|
||
VARCHAR id PK
|
||
VARCHAR name
|
||
VARCHAR status
|
||
}
|
||
permissions {
|
||
UUID id PK
|
||
VARCHAR code
|
||
TEXT description
|
||
VARCHAR resource
|
||
VARCHAR action
|
||
}
|
||
users {
|
||
UUID id PK
|
||
UUID tenant_id FK
|
||
VARCHAR email
|
||
}
|
||
roles {
|
||
UUID id PK
|
||
UUID tenant_id FK
|
||
VARCHAR name
|
||
BOOLEAN is_system
|
||
}
|
||
role_permissions {
|
||
UUID role_id FK
|
||
UUID permission_id FK
|
||
}
|
||
user_roles {
|
||
UUID user_id FK
|
||
UUID role_id FK
|
||
}
|
||
|
||
apps ||--o{ permissions : namespaces
|
||
roles ||--o{ role_permissions : grants
|
||
permissions ||--o{ role_permissions : assigned
|
||
users ||--o{ user_roles : has
|
||
roles ||--o{ user_roles : assigned
|
||
```
|
||
|
||
应用区分方式:
|
||
- 通过 `apps`(app_registry)维护“允许的应用标识”,并要求权限码的 `app_code` 与 `apps.id` 一致。
|
||
- 在租户侧通过 `enabled_apps` 控制某租户开通哪些应用;登录/权限查询会按 `enabled_apps` 过滤应用级权限(例如 `cms:*:*` 会在租户未开通 `cms` 时被过滤)。
|
||
|
||
### 1) 为 CMS 添加最小必要权限(SQL)
|
||
|
||
脚本位置:`scripts/db/migrations/0006_cms_permissions.sql`
|
||
|
||
包含权限项:
|
||
- 文章:创建、编辑、发布
|
||
- 栏目:管理
|
||
- 媒体库:管理
|
||
- 系统配置:管理
|
||
|
||
### 2) 查询权限列表(分页/搜索)
|
||
|
||
**GET** `/permissions?page=1&page_size=20&app_code=cms&search=article`
|
||
|
||
- Tag:`Permission`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- 需要权限:`role:read`
|
||
|
||
### 3) 角色 CRUD(Role)
|
||
|
||
创建角色:
|
||
- **POST** `/roles`
|
||
|
||
查询角色详情:
|
||
- **GET** `/roles/{id}`
|
||
|
||
更新角色:
|
||
- **PATCH** `/roles/{id}`
|
||
|
||
删除角色:
|
||
- **DELETE** `/roles/{id}`
|
||
|
||
说明:
|
||
- 系统内置角色(`is_system=true`,如 `Admin`)不允许通过 API 修改/删除。
|
||
|
||
### 4) 为角色批量绑定/解绑权限
|
||
|
||
绑定权限(批量):
|
||
- **POST** `/roles/{id}/permissions/grant`
|
||
- Body:
|
||
|
||
```json
|
||
{ "permission_codes": ["cms:article:create", "cms:article:publish"] }
|
||
```
|
||
|
||
解绑权限(批量):
|
||
- **POST** `/roles/{id}/permissions/revoke`
|
||
|
||
### 5) 批量给用户授予/回收角色
|
||
|
||
批量授予:
|
||
- **POST** `/roles/{id}/users/grant`
|
||
- Body:
|
||
|
||
```json
|
||
{ "user_ids": ["<user_id_1>", "<user_id_2>"] }
|
||
```
|
||
|
||
批量回收:
|
||
- **POST** `/roles/{id}/users/revoke`
|
||
|
||
审计说明:
|
||
- 角色创建/更新/删除、角色-权限绑定/解绑、角色-用户授予/回收都会写入 `audit_logs`。
|
||
|
||
### Step 7:用户-角色绑定(User)
|
||
|
||
用户注册后默认无角色;通常由具备 `user:write` 的管理员进行角色分配。
|
||
|
||
#### 7.1 查询用户角色列表(需要 user:read)
|
||
|
||
**GET** `/users/{id}/roles`
|
||
|
||
- Tag:`User`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
|
||
#### 7.2 设置用户角色(全量覆盖,幂等;需要 user:write)
|
||
|
||
**PUT** `/users/{id}/roles`
|
||
|
||
- Tag:`User`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- Body:
|
||
|
||
```json
|
||
{ "role_ids": ["<role_id_1>", "<role_id_2>"] }
|
||
```
|
||
|
||
说明:
|
||
- `role_ids` 必须全部属于当前租户,否则返回 400。
|
||
|
||
## 限流说明(Auth)
|
||
|
||
- `/auth/login`:约 2 req/s,burst 10(同一 IP)
|
||
- `/auth/register`:约 1 req/s,burst 5(同一 IP)
|
||
- 触发后返回:HTTP 429 + `code=40000`
|
||
|
||
## 密码重置(User)
|
||
|
||
用户自助重置(需要旧密码):
|
||
|
||
- **POST** `/users/me/password/reset`
|
||
- Tag:`User`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- Body:
|
||
|
||
```json
|
||
{ "current_password": "oldPassword123", "new_password": "newPassword456" }
|
||
```
|
||
|
||
租户管理员重置任意用户(生成临时密码):
|
||
|
||
- **POST** `/users/{id}/password/reset`
|
||
- Tag:`User`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
- 权限:需要 `user:password:reset:any`
|
||
- 二次验证(可选但建议生产启用):
|
||
- 若设置环境变量 `IAM_SENSITIVE_ACTION_TOKEN`,则必须传 Header:`X-Sensitive-Token: <token>`,否则返回 403。
|