Files
huang 9c399df6bc feat(auth): 新增系统启动时自动初始化默认超级管理员功能
- 新增默认管理员自动初始化逻辑,系统启动时检查并创建超级管理员账号
- 支持通过配置文件自定义账号信息(优先级:配置文件 > 代码默认值)
- 新增 CreateSystemAccount 方法用于系统内部账号创建
- 新增默认管理员配置项和常量定义
- 更新 README.md 添加默认账号使用说明
- 归档 OpenSpec 变更提案及完整文档

相关文件:
- internal/bootstrap/admin.go: 管理员初始化逻辑
- internal/service/account/service.go: 系统账号创建方法
- pkg/config/config.go: 默认管理员配置结构
- pkg/constants/constants.go: 默认值常量定义
- docs/add-default-admin-init/功能说明.md: 完整功能文档
2026-01-14 10:53:42 +08:00

216 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# auth Specification
## Purpose
TBD - created by archiving change refactor-framework-cleanup. Update Purpose after archive.
## Requirements
### Requirement: Unified Authentication Middleware
系统 SHALL 提供统一的认证中间件,支持可配置的 Token 提取和验证。
#### Scenario: Token 验证成功
- **WHEN** 请求携带有效的 Token
- **THEN** 中间件提取并验证 Token
- **AND** 将用户信息同时设置到 Fiber Locals 和 Context
- **AND** 请求继续执行
#### Scenario: Token 缺失
- **WHEN** 请求未携带 Token
- **AND** 路径不在跳过列表中
- **THEN** 返回 AppErrorCodeMissingToken
- **AND** 由全局 ErrorHandler 处理错误响应
#### Scenario: Token 无效
- **WHEN** 请求携带的 Token 无效或过期
- **THEN** 返回 AppErrorCodeUnauthorized
- **AND** 由全局 ErrorHandler 处理错误响应
#### Scenario: 跳过路径
- **WHEN** 请求路径在 SkipPaths 配置中
- **THEN** 中间件跳过认证
- **AND** 请求直接继续执行
### Requirement: User Context Management
认证中间件 SHALL 提供用户上下文管理函数,支持从 Context 获取用户信息。
#### Scenario: 获取用户 ID
- **WHEN** 调用 GetUserIDFromContext(ctx)
- **AND** 认证已通过
- **THEN** 返回当前用户的 ID
#### Scenario: 检查 Root 用户
- **WHEN** 调用 IsRootUser(ctx)
- **THEN** 返回当前用户是否为 Root 用户
#### Scenario: 设置用户到 Fiber Context
- **WHEN** 调用 SetUserToFiberContext(c, userInfo)
- **THEN** 用户信息被设置到 Fiber Locals
- **AND** 用户信息被设置到请求 Context供 GORM 等使用)
### Requirement: Auth Middleware Configuration
认证中间件 SHALL 支持灵活的配置选项。
#### Scenario: 自定义 Token 提取
- **WHEN** 配置了 TokenExtractor 函数
- **THEN** 使用自定义函数从请求中提取 Token
#### Scenario: 默认 Token 提取
- **WHEN** 未配置 TokenExtractor
- **THEN** 从 Authorization Header 提取 Bearer Token
#### Scenario: 自定义验证函数
- **WHEN** 配置了 Validator 函数
- **THEN** 使用自定义函数验证 Token 并返回用户信息
### Requirement: 启动时自动初始化默认管理员
系统在 API 服务启动时 SHALL 检查数据库是否存在超级管理员账号,如果不存在则自动创建默认管理员账号。
**业务规则**
- 检查条件:`user_type = 1`(超级管理员)且未被软删除的账号
- 仅在不存在时创建,存在管理员时跳过
- 默认账号信息读取优先级:
1. **配置文件优先**:读取 `config.yaml``default_admin` 配置节
2. **代码默认值**:如果配置文件未提供,使用代码内置常量
- 代码内置默认值:
- 用户名:`admin`
- 密码:`Admin@123456`bcrypt 哈希存储)
- 手机号:`13800000000`
- 用户类型:`1`(超级管理员)
- 状态:`1`(启用)
- 初始化失败不中断服务启动(记录错误日志,降级处理)
#### Scenario: 空数据库首次启动(使用代码默认值)
- **WHEN** API 服务启动且数据库中不存在任何超级管理员账号
- **AND** 配置文件未提供 `default_admin` 配置
- **THEN** 系统使用代码内置默认值创建管理员账号
- **AND** 用户名为 `admin`,密码为 `Admin@123456`,手机号为 `13800000000`
- **AND** 记录日志:"已创建默认管理员账号: admin使用代码默认值"
- **AND** 创建的账号可以正常使用(密码验证通过)
#### Scenario: 空数据库首次启动(使用配置文件)
- **WHEN** API 服务启动且数据库中不存在任何超级管理员账号
- **AND** 配置文件提供了 `default_admin` 配置
- **THEN** 系统使用配置文件中的值创建管理员账号
- **AND** 用户名、密码、手机号均从配置文件读取
- **AND** 记录日志:"已创建默认管理员账号: {username}(使用配置文件)"
- **AND** 创建的账号可以正常使用(配置的密码验证通过)
#### Scenario: 已有管理员时启动
- **WHEN** API 服务启动且数据库中已存在至少一个超级管理员账号
- **THEN** 系统跳过创建默认管理员
- **AND** 记录日志:"检测到已有管理员账号,跳过初始化"
- **AND** 不创建任何新账号
#### Scenario: 用户名或手机号冲突
- **WHEN** API 服务启动且尝试创建默认管理员
- **AND** 数据库中已存在用户名为 `admin` 或手机号为 `13800000000` 的账号(非超级管理员)
- **THEN** 系统创建失败
- **AND** 记录错误日志:"创建默认管理员失败: 用户名或手机号已存在"
- **AND** 不中断服务启动(降级处理)
#### Scenario: 初始化执行时机
- **WHEN** API 服务执行启动流程
- **THEN** 管理员初始化在以下时机执行:
1. 所有组件Store、Service、Handler初始化完成后
2. 注册路由前
3. 服务器开始监听前
- **AND** 确保 AccountStore 可用时才执行初始化
### Requirement: 默认管理员配置支持
系统 SHALL 支持通过配置文件自定义默认管理员账号信息,配置文件优先级高于代码默认值。
**配置格式**
```yaml
default_admin:
username: "admin" # 可选,默认 "admin"
password: "Admin@123456" # 可选,默认 "Admin@123456"
phone: "13800000000" # 可选,默认 "13800000000"
```
#### Scenario: 配置文件完整提供
- **WHEN** `config.yaml` 中配置了 `default_admin`
- **AND** 提供了 `username``password``phone` 三个字段
- **THEN** 系统读取配置文件的值
- **AND** 不使用代码默认值
- **AND** 创建管理员账号时使用配置的值
#### Scenario: 配置文件部分提供
- **WHEN** `config.yaml` 中配置了 `default_admin`
- **AND** 只提供了部分字段(如只配置了 `password`
- **THEN** 系统对已提供的字段使用配置值
- **AND** 对未提供的字段使用代码默认值
- **AND** 例如:配置了 `password: "MySecret123"`,但未配置 `username``phone`
- 使用 `password = "MySecret123"`
- 使用 `username = "admin"`(代码默认值)
- 使用 `phone = "13800000000"`(代码默认值)
#### Scenario: 配置文件未提供
- **WHEN** `config.yaml` 中未配置 `default_admin`
- **THEN** 系统使用代码内置默认值
- **AND** 用户名为 `admin`
- **AND** 密码为 `Admin@123456`
- **AND** 手机号为 `13800000000`
#### Scenario: 配置验证
- **WHEN** 读取 `default_admin` 配置
- **THEN** 配置项为可选,不参与 `Validate()` 验证
- **AND** 允许配置为空或不存在
- **AND** 不阻止服务启动
### Requirement: 默认管理员安全配置
系统 SHALL 使用足够复杂的默认密码,并记录管理员创建日志用于安全审计。
#### Scenario: 默认密码复杂度
- **WHEN** 创建默认管理员账号
- **THEN** 代码内置默认密码 SHALL 满足以下复杂度要求:
- 长度 ≥ 12 位
- 包含大写字母、小写字母、数字、特殊字符
- 示例:`Admin@123456`
#### Scenario: 审计日志记录
- **WHEN** 创建或跳过默认管理员账号
- **THEN** 系统记录审计日志到 `app.log`
- **AND** 日志包含以下信息:
- 操作时间
- 操作结果(创建成功/跳过/失败)
- 创建的用户名(成功时)
- 配置来源(配置文件/代码默认值)
- 失败原因(失败时)
- **AND** 不在日志中记录明文密码
### Requirement: 系统账号创建内部接口
Account Service SHALL 提供内部方法用于系统初始化场景创建账号,绕过常规的用户上下文检查。
#### Scenario: 系统初始化创建账号
- **WHEN** 系统初始化需要创建内部账号(如默认管理员)
- **THEN** 调用 `createSystemAccount(ctx, account)` 方法
- **AND** 该方法不检查当前用户 ID允许 context 中无用户信息)
- **AND** 保留用户名和手机号唯一性检查
- **AND** 密码使用 bcrypt 哈希存储
- **AND** 自动设置 creator 和 updater 为 0系统创建
#### Scenario: 常规 API 请求不使用系统接口
- **WHEN** 通过 HTTP API 创建账号
- **THEN** 使用常规 `Create()` 方法
- **AND** 必须有当前用户上下文user_id > 0
- **AND** 不允许调用 `createSystemAccount()` 方法(内部使用)