Files
SatoNano/docs/architecture.md
2026-01-06 23:49:23 +08:00

304 lines
9.2 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.

# 认证系统架构设计文档
## 技术栈
| 组件 | 技术选型 | 说明 |
|------|----------|------|
| Web 框架 | FastAPI 0.128+ | 异步、高性能、自动 OpenAPI 文档 |
| ORM | SQLAlchemy 2.0+ | 异步支持、类型安全 |
| 数据库 | SQLite (aiosqlite) | 开发环境,可切换 PostgreSQL |
| 密码哈希 | Argon2 | 密码学家推荐的哈希算法 |
| 令牌 | PyJWT | JSON Web Token 实现 |
| 数据验证 | Pydantic v2 | 高性能数据验证 |
| 配置管理 | pydantic-settings | 类型安全的环境变量配置 |
## 项目结构
```
app/
├── __init__.py
├── main.py # FastAPI 应用入口
├── database.py # 数据库连接与会话管理
├── core/ # 核心功能模块
│ ├── config.py # 配置管理
│ ├── security.py # 安全功能密码哈希、JWT
│ └── exceptions.py # 自定义异常类
├── models/ # SQLAlchemy ORM 模型
│ └── user.py # 用户数据模型
├── schemas/ # Pydantic 数据模式
│ ├── base.py # 基础响应格式
│ ├── user.py # 用户相关 Schema
│ └── auth.py # 认证相关 Schema
├── repositories/ # 数据访问层 (Repository Pattern)
│ ├── base.py # 基础 CRUD 操作
│ └── user.py # 用户数据仓库
├── services/ # 业务逻辑层 (Service Layer)
│ ├── user.py # 用户服务
│ └── auth.py # 认证服务
└── api/ # API 路由层
├── deps.py # 依赖注入定义
└── v1/
├── router.py # 路由聚合
└── endpoints/
├── auth.py # 认证接口
└── users.py # 用户接口
```
## 分层架构
```
┌─────────────────────────────────────────────┐
│ API Layer │
│ (FastAPI Endpoints + Deps) │
├─────────────────────────────────────────────┤
│ Service Layer │
│ (Business Logic + Rules) │
├─────────────────────────────────────────────┤
│ Repository Layer │
│ (Data Access + Queries) │
├─────────────────────────────────────────────┤
│ Model Layer │
│ (SQLAlchemy ORM Models) │
├─────────────────────────────────────────────┤
│ Database │
│ (SQLite / PostgreSQL) │
└─────────────────────────────────────────────┘
```
### 各层职责
| 层 | 职责 | 示例 |
|----|------|------|
| **API Layer** | 处理 HTTP 请求/响应、参数验证、依赖注入 | `auth.py`, `users.py` |
| **Service Layer** | 业务逻辑、规则校验、跨仓库协调 | `AuthService`, `UserService` |
| **Repository Layer** | 数据库操作封装、查询构建 | `UserRepository` |
| **Model Layer** | 数据结构定义、表关系映射 | `User` |
## 数据流
### 用户注册流程
```
Client Request
┌─────────────────┐
│ API Endpoint │ POST /api/v1/auth/register
│ (auth.py) │ 接收请求、验证 Schema
└────────┬────────┘
│ UserCreate
┌─────────────────┐
│ UserService │ 检查用户名/邮箱唯一性
│ (user.py) │ 哈希密码
└────────┬────────┘
┌─────────────────┐
│ UserRepository │ 创建用户记录
│ (user.py) │ 提交事务
└────────┬────────┘
┌─────────────────┐
│ Database │ INSERT INTO users
└─────────────────┘
```
### 用户登录流程
```
Client Request
┌─────────────────┐
│ API Endpoint │ POST /api/v1/auth/login
│ (auth.py) │ 接收凭证
└────────┬────────┘
│ LoginRequest
┌─────────────────┐
│ AuthService │ 1. 查找用户
│ (auth.py) │ 2. 验证密码
│ │ 3. 检查用户状态
│ │ 4. 更新登录时间
│ │ 5. 生成 JWT
└────────┬────────┘
┌─────────────────┐
│ UserRepository │ 查询用户
│ (user.py) │ 更新 last_login_at
└────────┬────────┘
TokenResponse
```
### 认证流程 (受保护接口)
```
Client Request + Bearer Token
┌─────────────────┐
│ HTTPBearer │ 提取 Authorization header
│ (FastAPI) │
└────────┬────────┘
┌─────────────────┐
│ get_current_user│ 1. 解码 JWT
│ (deps.py) │ 2. 验证签名和过期
│ │ 3. 查询用户
│ │ 4. 检查用户状态
└────────┬────────┘
│ User
┌─────────────────┐
│ API Endpoint │ 使用已认证用户执行业务逻辑
└─────────────────┘
```
## 安全设计
### 密码存储
使用 **Argon2id** 算法(密码哈希竞赛获胜算法):
```python
# 参数配置
time_cost=3 # 迭代次数
memory_cost=65536 # 内存使用 (64MB)
parallelism=4 # 并行度
```
特性:
- 抗 GPU/ASIC 攻击
- 可调参数适应硬件升级
- 自动包含盐值
### JWT 令牌
**Access Token:**
- 算法HS256
- 有效期30 分钟(可配置)
- 包含:用户 ID、用户名、角色
**Refresh Token:**
- 算法HS256
- 有效期7 天(可配置)
- 仅包含:用户 ID
### 防护措施
| 威胁 | 防护 |
|------|------|
| 暴力破解 | Argon2 计算成本 |
| 时序攻击 | 无论用户是否存在都执行密码验证 |
| Token 泄露 | 短期 Access Token + 长期 Refresh Token |
| SQL 注入 | SQLAlchemy 参数化查询 |
| XSS | JSON 响应(非 HTML |
## 扩展点
### 邀请码注册
```python
# schemas/user.py
class UserCreate(UserBase):
password: str
invite_code: str | None = None # 新增字段
# services/user.py
async def create_user(self, user_data: UserCreate) -> User:
if settings.require_invite_code:
await self._validate_invite_code(user_data.invite_code)
# ... 原有逻辑
```
### OAuth2 登录
```python
# services/oauth.py
class OAuthService:
async def authenticate_google(self, code: str) -> User:
...
async def authenticate_github(self, code: str) -> User:
...
# api/v1/endpoints/oauth.py
@router.get("/google/callback")
async def google_callback(code: str, oauth: OAuthService = Depends()):
user = await oauth.authenticate_google(code)
tokens = auth_service.create_tokens(user)
return tokens
```
### 令牌黑名单
```python
# models/token_blacklist.py
class TokenBlacklist(Base):
__tablename__ = "token_blacklist"
jti: Mapped[str] = mapped_column(primary_key=True)
expires_at: Mapped[datetime]
# services/auth.py
async def logout(self, token: str) -> None:
payload = decode_token(token)
await self.blacklist_repo.add(payload["jti"], payload["exp"])
```
## 配置参考
```bash
# .env 配置示例
# 环境
ENVIRONMENT=development # development | staging | production
DEBUG=true
# 安全
SECRET_KEY=your-256-bit-secret-key
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_DAYS=7
# 数据库
DATABASE_URL=sqlite+aiosqlite:///./satonano.db
# PostgreSQL: postgresql+asyncpg://user:pass@localhost/dbname
# 密码策略
PASSWORD_MIN_LENGTH=8
PASSWORD_REQUIRE_UPPERCASE=true
PASSWORD_REQUIRE_LOWERCASE=true
PASSWORD_REQUIRE_DIGIT=true
PASSWORD_REQUIRE_SPECIAL=false
```
## 测试建议
```bash
# 安装测试依赖
uv add --dev pytest pytest-asyncio httpx
# 运行测试
uv run pytest tests/ -v
```
测试覆盖点:
- 用户注册(正常、重复用户名、弱密码)
- 用户登录(正常、错误密码、禁用用户)
- 令牌刷新(正常、过期令牌、无效令牌)
- 密码修改(正常、错误当前密码)
- 权限验证(未认证、已认证、管理员)