304 lines
9.2 KiB
Markdown
304 lines
9.2 KiB
Markdown
# 认证系统架构设计文档
|
||
|
||
## 技术栈
|
||
|
||
| 组件 | 技术选型 | 说明 |
|
||
|------|----------|------|
|
||
| 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
|
||
```
|
||
|
||
测试覆盖点:
|
||
- 用户注册(正常、重复用户名、弱密码)
|
||
- 用户登录(正常、错误密码、禁用用户)
|
||
- 令牌刷新(正常、过期令牌、无效令牌)
|
||
- 密码修改(正常、错误当前密码)
|
||
- 权限验证(未认证、已认证、管理员)
|
||
|