feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
185
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/api_contract.yaml
Normal file
185
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/api_contract.yaml
Normal file
@@ -0,0 +1,185 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: 认证授权模块API
|
||||
version: 1.0.0
|
||||
description: 负责用户认证、授权和Token管理
|
||||
|
||||
paths:
|
||||
/api/v1/auth/login:
|
||||
post:
|
||||
summary: 用户登录
|
||||
tags: [认证]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: 用户名或邮箱
|
||||
password:
|
||||
type: string
|
||||
description: 密码
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
responses:
|
||||
200:
|
||||
description: 登录成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TokenResponse'
|
||||
400:
|
||||
description: 请求参数错误
|
||||
401:
|
||||
description: 用户名或密码错误
|
||||
403:
|
||||
description: 账号已被禁用
|
||||
|
||||
/api/v1/auth/register:
|
||||
post:
|
||||
summary: 用户注册
|
||||
tags: [认证]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserRegister'
|
||||
responses:
|
||||
201:
|
||||
description: 注册成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TokenResponse'
|
||||
400:
|
||||
description: 参数验证失败
|
||||
409:
|
||||
description: 用户名或邮箱已存在
|
||||
|
||||
/api/v1/auth/logout:
|
||||
post:
|
||||
summary: 用户登出
|
||||
tags: [认证]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
200:
|
||||
description: 登出成功
|
||||
401:
|
||||
description: 未授权
|
||||
|
||||
/api/v1/auth/refresh:
|
||||
post:
|
||||
summary: 刷新Token
|
||||
tags: [认证]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
required:
|
||||
- refresh_token
|
||||
responses:
|
||||
200:
|
||||
description: 刷新成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TokenResponse'
|
||||
401:
|
||||
description: 刷新Token无效
|
||||
|
||||
/api/v1/auth/reset-password:
|
||||
post:
|
||||
summary: 重置密码请求
|
||||
tags: [认证]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
required:
|
||||
- email
|
||||
responses:
|
||||
200:
|
||||
description: 重置邮件已发送
|
||||
404:
|
||||
description: 邮箱不存在
|
||||
|
||||
components:
|
||||
schemas:
|
||||
UserRegister:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
minLength: 3
|
||||
maxLength: 20
|
||||
pattern: '^[a-zA-Z0-9_-]+$'
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
password:
|
||||
type: string
|
||||
minLength: 8
|
||||
confirm_password:
|
||||
type: string
|
||||
required:
|
||||
- username
|
||||
- email
|
||||
- password
|
||||
- confirm_password
|
||||
|
||||
TokenResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
example: 200
|
||||
message:
|
||||
type: string
|
||||
example: success
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
refresh_token:
|
||||
type: string
|
||||
token_type:
|
||||
type: string
|
||||
example: bearer
|
||||
expires_in:
|
||||
type: integer
|
||||
example: 1800
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
username:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
role:
|
||||
type: string
|
||||
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
119
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/checklist.md
Normal file
119
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/checklist.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Agent-Auth 开发检查清单
|
||||
|
||||
## 开发前准备
|
||||
- [ ] 阅读并理解 `prompt.md` 中的所有要求
|
||||
- [ ] 熟悉 `context.md` 中的项目结构和依赖
|
||||
- [ ] 查看 `api_contract.yaml` 了解API规范
|
||||
- [ ] 设置开发环境,安装所有依赖
|
||||
- [ ] 创建 feature/auth 分支
|
||||
|
||||
## 核心功能开发
|
||||
|
||||
### 1. 数据库模型
|
||||
- [ ] 创建 `app/models/user.py` 用户模型
|
||||
- [ ] 继承 BaseModel 和 AuditMixin
|
||||
- [ ] 添加必要的索引
|
||||
- [ ] 创建数据库迁移脚本
|
||||
|
||||
### 2. Schema定义
|
||||
- [ ] 创建 `app/schemas/auth.py`
|
||||
- [ ] 定义 UserLogin schema
|
||||
- [ ] 定义 UserRegister schema
|
||||
- [ ] 定义 Token schema
|
||||
- [ ] 添加输入验证规则
|
||||
|
||||
### 3. 安全功能
|
||||
- [ ] 完善 `app/core/security.py`
|
||||
- [ ] 实现密码加密函数
|
||||
- [ ] 实现密码验证函数
|
||||
- [ ] 实现JWT Token生成
|
||||
- [ ] 实现JWT Token验证
|
||||
|
||||
### 4. 依赖注入
|
||||
- [ ] 创建/完善 `app/api/deps.py`
|
||||
- [ ] 实现 get_current_user
|
||||
- [ ] 实现 get_current_active_user
|
||||
- [ ] 实现角色检查器(require_admin等)
|
||||
|
||||
### 5. 业务服务
|
||||
- [ ] 创建 `app/services/auth_service.py`
|
||||
- [ ] 实现用户认证逻辑
|
||||
- [ ] 实现用户创建逻辑
|
||||
- [ ] 实现Token管理逻辑
|
||||
- [ ] 添加错误处理
|
||||
|
||||
### 6. API路由
|
||||
- [ ] 创建 `app/api/v1/auth.py`
|
||||
- [ ] 实现登录端点
|
||||
- [ ] 实现注册端点
|
||||
- [ ] 实现登出端点
|
||||
- [ ] 实现Token刷新端点
|
||||
- [ ] 实现密码重置端点
|
||||
|
||||
### 7. 中间件和异常
|
||||
- [ ] 实现认证中间件
|
||||
- [ ] 添加请求限流
|
||||
- [ ] 处理认证异常
|
||||
|
||||
## 测试开发
|
||||
|
||||
### 单元测试
|
||||
- [ ] 创建 `tests/unit/test_auth_service.py`
|
||||
- [ ] 测试密码加密和验证
|
||||
- [ ] 测试Token生成和验证
|
||||
- [ ] 测试用户认证逻辑
|
||||
- [ ] 测试用户创建逻辑
|
||||
|
||||
### 集成测试
|
||||
- [ ] 创建 `tests/integration/test_auth_api.py`
|
||||
- [ ] 测试登录流程
|
||||
- [ ] 测试注册流程
|
||||
- [ ] 测试Token刷新
|
||||
- [ ] 测试权限验证
|
||||
|
||||
### 性能测试
|
||||
- [ ] 测试登录响应时间
|
||||
- [ ] 测试Token验证性能
|
||||
- [ ] 测试并发登录
|
||||
|
||||
## 安全检查
|
||||
- [ ] 密码使用bcrypt加密
|
||||
- [ ] Token设置合理过期时间
|
||||
- [ ] 实现登录失败限制
|
||||
- [ ] 防止暴力破解
|
||||
- [ ] SQL注入防护
|
||||
- [ ] XSS防护
|
||||
|
||||
## 文档更新
|
||||
- [ ] 更新API文档
|
||||
- [ ] 添加使用示例
|
||||
- [ ] 更新README
|
||||
- [ ] 编写部署说明
|
||||
|
||||
## 代码质量
|
||||
- [ ] 运行 `make format` 格式化代码
|
||||
- [ ] 运行 `make lint` 检查代码风格
|
||||
- [ ] 运行 `make type-check` 类型检查
|
||||
- [ ] 运行 `make test` 所有测试通过
|
||||
- [ ] 代码覆盖率 > 80%
|
||||
|
||||
## 集成验证
|
||||
- [ ] 与其他模块集成测试
|
||||
- [ ] 验证依赖注入正常工作
|
||||
- [ ] 验证中间件正常拦截
|
||||
- [ ] 验证日志记录完整
|
||||
|
||||
## 提交前确认
|
||||
- [ ] 所有功能已实现
|
||||
- [ ] 所有测试通过
|
||||
- [ ] 文档已更新
|
||||
- [ ] 代码已审查
|
||||
- [ ] 没有硬编码的密钥
|
||||
- [ ] 没有调试代码
|
||||
- [ ] commit信息符合规范
|
||||
|
||||
## 部署准备
|
||||
- [ ] 环境变量已配置
|
||||
- [ ] 生产配置已准备
|
||||
- [ ] 性能优化已完成
|
||||
- [ ] 监控指标已添加
|
||||
137
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/context.md
Normal file
137
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/context.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Agent-Auth 上下文信息
|
||||
|
||||
## 重要规划文档
|
||||
在开始开发前,请确保你已经理解以下关键文档:
|
||||
- `../../协作机制设计.md` - 特别是全局上下文(GlobalContext)和服务间调用机制
|
||||
- `../../模块分工指南.md` - 了解Auth模块的职责边界(第2.1节)
|
||||
- `../../开发规范文档.md` - 编码标准和API设计规范
|
||||
- `../../统一基础代码.md` - 可复用的代码模板
|
||||
|
||||
## 项目位置
|
||||
|
||||
- 项目根目录: `/Users/nongjun/Desktop/Ai公司/本地开发与测试/kaopeilian-backend/`
|
||||
- 你的工作目录: `app/api/v1/auth.py`, `app/core/security.py`, `app/services/auth_service.py`
|
||||
|
||||
## 重要依赖文件
|
||||
|
||||
### 1. 基础模型 (`app/models/base.py`)
|
||||
|
||||
已提供BaseModel, SoftDeleteMixin, AuditMixin等基类
|
||||
|
||||
### 2. 用户模型 (`app/models/user.py`) - 需要你创建
|
||||
|
||||
```python
|
||||
from sqlalchemy import Column, String, Boolean, Enum
|
||||
from app.models.base import BaseModel, AuditMixin
|
||||
|
||||
class User(BaseModel, AuditMixin):
|
||||
__tablename__ = "users"
|
||||
|
||||
username = Column(String(50), unique=True, nullable=False, index=True)
|
||||
email = Column(String(100), unique=True, nullable=False, index=True)
|
||||
password_hash = Column(String(200), nullable=False)
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_superuser = Column(Boolean, default=False)
|
||||
role = Column(String(20), default="trainee") # trainee, manager, admin
|
||||
```
|
||||
|
||||
### 3. 基础Schema (`app/schemas/base.py`)
|
||||
|
||||
已提供BaseSchema, ResponseModel, ErrorResponse等基类
|
||||
|
||||
### 4. 异常处理 (`app/core/exceptions.py`)
|
||||
|
||||
已定义所有标准异常类
|
||||
|
||||
### 5. 日志系统 (`app/core/logger.py`)
|
||||
|
||||
已配置结构化日志
|
||||
|
||||
### 6. 配置管理 (`app/config/settings.py`)
|
||||
|
||||
关键配置项:
|
||||
|
||||
- SECRET_KEY - JWT密钥
|
||||
- ALGORITHM - JWT算法(默认HS256)
|
||||
- ACCESS_TOKEN_EXPIRE_MINUTES - 访问Token过期时间(默认30分钟)
|
||||
- REFRESH_TOKEN_EXPIRE_DAYS - 刷新Token过期时间(默认7天)
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
email VARCHAR(100) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(200) NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
is_superuser BOOLEAN DEFAULT FALSE,
|
||||
role VARCHAR(20) DEFAULT 'trainee',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
created_by BIGINT,
|
||||
updated_by BIGINT,
|
||||
INDEX idx_username (username),
|
||||
INDEX idx_email (email)
|
||||
);
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
```bash
|
||||
# .env 文件中的认证相关配置
|
||||
SECRET_KEY="your-secret-key-here-must-be-at-least-32-chars"
|
||||
ALGORITHM="HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
```
|
||||
|
||||
## 测试账号
|
||||
|
||||
用于开发测试的默认账号:
|
||||
|
||||
- 超级管理员: superadmin / Superadmin123!
|
||||
- 系统管理员: admin / Admin123!
|
||||
- 测试学员: testuser / TestPass123!
|
||||
|
||||
## 错误码约定
|
||||
|
||||
- 1001: 用户名或密码错误
|
||||
- 1002: 账号已被禁用
|
||||
- 1003: Token无效或已过期
|
||||
- 1004: 权限不足
|
||||
- 1005: 用户名已存在
|
||||
- 1006: 邮箱已存在
|
||||
|
||||
## 关键流程
|
||||
|
||||
### 登录流程
|
||||
|
||||
1. 接收用户名和密码
|
||||
2. 验证用户身份
|
||||
3. 检查账号状态
|
||||
4. 生成访问Token和刷新Token
|
||||
5. 返回Token信息
|
||||
|
||||
### 注册流程
|
||||
|
||||
1. 验证输入数据
|
||||
2. 检查用户名和邮箱唯一性
|
||||
3. 加密密码
|
||||
4. 创建用户记录
|
||||
5. 自动登录并返回Token
|
||||
|
||||
### Token刷新流程
|
||||
|
||||
1. 验证刷新Token
|
||||
2. 检查Token是否在黑名单
|
||||
3. 生成新的访问Token
|
||||
4. 可选:轮换刷新Token
|
||||
|
||||
## 安全最佳实践
|
||||
|
||||
1. 使用bcrypt加密密码,cost factor设为12
|
||||
2. JWT Token使用RS256算法(生产环境)
|
||||
3. 实现Token黑名单机制(使用Redis)
|
||||
4. 登录失败5次锁定账号15分钟
|
||||
5. 敏感操作记录审计日志
|
||||
93
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/dependencies.md
Normal file
93
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/dependencies.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Agent-Auth 依赖关系
|
||||
|
||||
## 依赖概览
|
||||
Agent-Auth是基础模块,**不依赖其他业务模块**,但依赖系统基础设施。
|
||||
|
||||
## 输入依赖
|
||||
|
||||
### 1. 系统基础设施
|
||||
- `app/config/settings.py` - 系统配置
|
||||
- `app/config/database.py` - 数据库连接
|
||||
- `app/core/logger.py` - 日志系统
|
||||
- `app/models/base.py` - 基础模型类
|
||||
- `app/schemas/base.py` - 基础Schema类
|
||||
|
||||
### 2. 第三方库
|
||||
- `fastapi` - Web框架
|
||||
- `sqlalchemy` - ORM
|
||||
- `passlib[bcrypt]` - 密码加密
|
||||
- `python-jose[cryptography]` - JWT处理
|
||||
- `redis` - 缓存(用于Token黑名单)
|
||||
|
||||
### 3. 环境变量
|
||||
```bash
|
||||
SECRET_KEY=必需
|
||||
ALGORITHM=可选(默认HS256)
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=可选(默认30)
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=可选(默认7)
|
||||
```
|
||||
|
||||
## 输出接口
|
||||
|
||||
### 1. 依赖注入函数
|
||||
其他所有模块都会使用这些函数:
|
||||
```python
|
||||
# app/api/deps.py 或 app/core/deps.py
|
||||
async def get_current_user() -> User
|
||||
async def get_current_active_user() -> User
|
||||
async def get_optional_current_user() -> Optional[User]
|
||||
|
||||
# 角色检查器
|
||||
require_admin = RoleChecker(["admin"])
|
||||
require_manager = RoleChecker(["admin", "manager"])
|
||||
require_trainer = RoleChecker(["admin", "manager", "trainer"])
|
||||
```
|
||||
|
||||
### 2. 安全工具函数
|
||||
```python
|
||||
# app/core/security.py
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool
|
||||
def get_password_hash(password: str) -> str
|
||||
def create_access_token(subject: int, **kwargs) -> str
|
||||
def create_refresh_token(subject: int) -> str
|
||||
def verify_token(token: str) -> dict
|
||||
```
|
||||
|
||||
### 3. 数据模型
|
||||
```python
|
||||
# app/models/user.py
|
||||
class User(BaseModel, AuditMixin):
|
||||
# 被所有其他模块引用的用户模型
|
||||
pass
|
||||
```
|
||||
|
||||
### 4. API端点
|
||||
```
|
||||
POST /api/v1/auth/login
|
||||
POST /api/v1/auth/register
|
||||
POST /api/v1/auth/logout
|
||||
POST /api/v1/auth/refresh
|
||||
POST /api/v1/auth/reset-password
|
||||
```
|
||||
|
||||
## 被依赖情况
|
||||
|
||||
以下模块依赖Auth模块:
|
||||
- **所有模块** - 使用认证和权限检查
|
||||
- Agent-User - 使用User模型
|
||||
- Agent-Course - 使用get_current_user
|
||||
- Agent-Exam - 使用get_current_user
|
||||
- Agent-Training - 使用get_current_user
|
||||
- Agent-Analytics - 使用权限检查
|
||||
- Agent-Admin - 使用require_admin
|
||||
|
||||
## 接口稳定性
|
||||
⚠️ **关键接口,需保持稳定**
|
||||
- 修改认证逻辑会影响所有模块
|
||||
- Token格式变更需要通知所有模块
|
||||
- User模型字段变更需要评估影响
|
||||
|
||||
## 测试依赖
|
||||
- 需要模拟Redis服务
|
||||
- 需要测试数据库
|
||||
- 需要模拟邮件服务(密码重置)
|
||||
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
认证API路由示例代码
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.core.exceptions import UnauthorizedError, ConflictError
|
||||
from app.schemas.auth import UserRegister, Token, PasswordReset
|
||||
from app.schemas.base import ResponseModel
|
||||
from app.services.auth_service import AuthService
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["认证"])
|
||||
|
||||
|
||||
@router.post("/login", response_model=ResponseModel[Token], summary="用户登录")
|
||||
async def login(
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
用户登录接口
|
||||
|
||||
- **username**: 用户名或邮箱
|
||||
- **password**: 密码
|
||||
|
||||
返回访问令牌和刷新令牌
|
||||
"""
|
||||
auth_service = AuthService(db)
|
||||
|
||||
# 验证用户
|
||||
user = await auth_service.authenticate_user(
|
||||
username=form_data.username,
|
||||
password=form_data.password
|
||||
)
|
||||
|
||||
if not user:
|
||||
raise UnauthorizedError("用户名或密码错误")
|
||||
|
||||
# 创建Token
|
||||
token = await auth_service.create_tokens(user)
|
||||
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="登录成功",
|
||||
data=token
|
||||
)
|
||||
|
||||
|
||||
@router.post("/register", response_model=ResponseModel[Token], status_code=status.HTTP_201_CREATED)
|
||||
async def register(
|
||||
user_data: UserRegister,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
用户注册接口
|
||||
|
||||
注册成功后自动登录,返回Token
|
||||
"""
|
||||
# 验证密码一致性
|
||||
if user_data.password != user_data.confirm_password:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="两次输入的密码不一致"
|
||||
)
|
||||
|
||||
auth_service = AuthService(db)
|
||||
|
||||
try:
|
||||
# 创建用户
|
||||
user = await auth_service.create_user(user_data)
|
||||
|
||||
# 自动登录
|
||||
token = await auth_service.create_tokens(user)
|
||||
|
||||
return ResponseModel(
|
||||
code=201,
|
||||
message="注册成功",
|
||||
data=token
|
||||
)
|
||||
except ConflictError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=str(e)
|
||||
)
|
||||
|
||||
|
||||
@router.post("/logout", response_model=ResponseModel)
|
||||
async def logout(
|
||||
token: str = Depends(get_current_token),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
用户登出接口
|
||||
|
||||
将当前Token加入黑名单
|
||||
"""
|
||||
auth_service = AuthService(db)
|
||||
await auth_service.logout(token)
|
||||
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="登出成功"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/refresh", response_model=ResponseModel[Token])
|
||||
async def refresh_token(
|
||||
refresh_token: str,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
刷新访问令牌
|
||||
|
||||
使用刷新令牌获取新的访问令牌
|
||||
"""
|
||||
auth_service = AuthService(db)
|
||||
|
||||
try:
|
||||
token = await auth_service.refresh_access_token(refresh_token)
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="刷新成功",
|
||||
data=token
|
||||
)
|
||||
except UnauthorizedError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="刷新令牌无效或已过期"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/reset-password/request", response_model=ResponseModel)
|
||||
async def request_password_reset(
|
||||
email: str,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
请求重置密码
|
||||
|
||||
向用户邮箱发送重置链接
|
||||
"""
|
||||
auth_service = AuthService(db)
|
||||
|
||||
# 查找用户
|
||||
user = await auth_service.get_user_by_email(email)
|
||||
if not user:
|
||||
# 为了安全,即使用户不存在也返回成功
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="如果邮箱存在,重置链接已发送"
|
||||
)
|
||||
|
||||
# 生成重置令牌
|
||||
reset_token = await auth_service.create_password_reset_token(user)
|
||||
|
||||
# TODO: 发送邮件
|
||||
# await send_reset_email(email, reset_token)
|
||||
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="如果邮箱存在,重置链接已发送"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/reset-password/confirm", response_model=ResponseModel)
|
||||
async def reset_password(
|
||||
reset_data: PasswordReset,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
确认重置密码
|
||||
|
||||
使用重置令牌设置新密码
|
||||
"""
|
||||
auth_service = AuthService(db)
|
||||
|
||||
try:
|
||||
await auth_service.reset_password(
|
||||
token=reset_data.token,
|
||||
new_password=reset_data.new_password
|
||||
)
|
||||
|
||||
return ResponseModel(
|
||||
code=200,
|
||||
message="密码重置成功"
|
||||
)
|
||||
except UnauthorizedError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="重置令牌无效或已过期"
|
||||
)
|
||||
|
||||
|
||||
# 辅助函数
|
||||
async def get_current_token(
|
||||
authorization: str = Depends(oauth2_scheme)
|
||||
) -> str:
|
||||
"""获取当前请求的Token"""
|
||||
return authorization
|
||||
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
认证服务示例代码
|
||||
"""
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from app.core.security import verify_password, get_password_hash, create_access_token, create_refresh_token
|
||||
from app.core.exceptions import UnauthorizedError, ConflictError, ForbiddenError
|
||||
from app.core.logger import logger
|
||||
from app.models.user import User
|
||||
from app.schemas.auth import UserRegister, Token
|
||||
|
||||
|
||||
class AuthService:
|
||||
"""认证服务类"""
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.db = db
|
||||
|
||||
async def authenticate_user(self, username: str, password: str) -> Optional[User]:
|
||||
"""
|
||||
验证用户身份
|
||||
|
||||
Args:
|
||||
username: 用户名或邮箱
|
||||
password: 密码
|
||||
|
||||
Returns:
|
||||
验证成功返回User对象,失败返回None
|
||||
"""
|
||||
# 查询用户(支持用户名或邮箱登录)
|
||||
query = select(User).where(
|
||||
(User.username == username) | (User.email == username)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
logger.warning("登录失败:用户不存在", username=username)
|
||||
return None
|
||||
|
||||
# 验证密码
|
||||
if not verify_password(password, user.password_hash):
|
||||
logger.warning("登录失败:密码错误", user_id=user.id, username=username)
|
||||
# TODO: 记录失败次数,实现账号锁定
|
||||
return None
|
||||
|
||||
# 检查账号状态
|
||||
if not user.is_active:
|
||||
logger.warning("登录失败:账号已禁用", user_id=user.id, username=username)
|
||||
raise ForbiddenError("账号已被禁用")
|
||||
|
||||
logger.info("用户登录成功", user_id=user.id, username=user.username)
|
||||
return user
|
||||
|
||||
async def create_user(self, user_data: UserRegister) -> User:
|
||||
"""
|
||||
创建新用户
|
||||
|
||||
Args:
|
||||
user_data: 用户注册数据
|
||||
|
||||
Returns:
|
||||
创建的用户对象
|
||||
|
||||
Raises:
|
||||
ConflictError: 用户名或邮箱已存在
|
||||
"""
|
||||
# 检查用户名是否存在
|
||||
query = select(User).where(User.username == user_data.username)
|
||||
result = await self.db.execute(query)
|
||||
if result.scalar_one_or_none():
|
||||
raise ConflictError("用户名已存在")
|
||||
|
||||
# 检查邮箱是否存在
|
||||
query = select(User).where(User.email == user_data.email)
|
||||
result = await self.db.execute(query)
|
||||
if result.scalar_one_or_none():
|
||||
raise ConflictError("邮箱已存在")
|
||||
|
||||
# 创建用户
|
||||
user = User(
|
||||
username=user_data.username,
|
||||
email=user_data.email,
|
||||
password_hash=get_password_hash(user_data.password),
|
||||
is_active=True,
|
||||
is_superuser=False,
|
||||
role="trainee" # 默认角色为学员
|
||||
)
|
||||
|
||||
self.db.add(user)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(user)
|
||||
|
||||
logger.info("用户注册成功", user_id=user.id, username=user.username)
|
||||
return user
|
||||
|
||||
async def create_tokens(self, user: User) -> Token:
|
||||
"""
|
||||
为用户创建访问令牌和刷新令牌
|
||||
|
||||
Args:
|
||||
user: 用户对象
|
||||
|
||||
Returns:
|
||||
Token对象
|
||||
"""
|
||||
# 创建访问令牌
|
||||
access_token = create_access_token(
|
||||
subject=user.id,
|
||||
role=user.role,
|
||||
username=user.username
|
||||
)
|
||||
|
||||
# 创建刷新令牌
|
||||
refresh_token = create_refresh_token(subject=user.id)
|
||||
|
||||
return Token(
|
||||
access_token=access_token,
|
||||
refresh_token=refresh_token,
|
||||
token_type="bearer",
|
||||
expires_in=1800, # 30分钟
|
||||
user={
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
"email": user.email,
|
||||
"role": user.role
|
||||
}
|
||||
)
|
||||
|
||||
async def logout(self, token: str) -> None:
|
||||
"""
|
||||
用户登出,将token加入黑名单
|
||||
|
||||
Args:
|
||||
token: 要失效的token
|
||||
"""
|
||||
# TODO: 将token加入Redis黑名单
|
||||
# redis_key = f"blacklist:{token}"
|
||||
# await redis.setex(redis_key, 3600, "1") # 设置1小时过期
|
||||
|
||||
logger.info("用户登出成功")
|
||||
|
||||
async def refresh_access_token(self, refresh_token: str) -> Token:
|
||||
"""
|
||||
使用刷新令牌获取新的访问令牌
|
||||
|
||||
Args:
|
||||
refresh_token: 刷新令牌
|
||||
|
||||
Returns:
|
||||
新的Token对象
|
||||
"""
|
||||
# TODO: 验证刷新令牌
|
||||
# TODO: 检查是否在黑名单
|
||||
# TODO: 生成新的访问令牌
|
||||
# TODO: 可选 - 轮换刷新令牌
|
||||
|
||||
pass
|
||||
188
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/prompt.md
Normal file
188
docs/规划/后端开发拆分策略/子agent/01-Agent-Auth/prompt.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Agent-Auth 提示词
|
||||
|
||||
## 基础规范引用
|
||||
**重要**: 开始开发前,你必须先阅读并严格遵循以下文件:
|
||||
- `00-通用基础/base_prompt.md` - 通用开发规范(代码格式、错误处理、日志规范等)
|
||||
- `00-通用基础/project_structure.md` - 项目目录结构说明
|
||||
|
||||
## 你的角色
|
||||
你是Agent-Auth,负责考培练系统的**认证授权模块**开发。你的代码将成为整个系统的安全基石,其他所有模块都将依赖你的认证服务。
|
||||
|
||||
## 核心职责
|
||||
1. 实现用户登录、注册、登出功能
|
||||
2. 管理JWT Token的生成和验证
|
||||
3. 提供权限检查中间件和依赖注入
|
||||
4. 实现密码重置和账号激活功能
|
||||
5. 确保系统的安全性
|
||||
|
||||
## 你需要开发的文件
|
||||
|
||||
### 1. API路由 (`app/api/v1/auth.py`)
|
||||
```python
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_db
|
||||
from app.services.auth_service import AuthService
|
||||
from app.schemas.auth import Token, UserLogin, UserRegister
|
||||
from app.schemas.base import ResponseModel
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/login", response_model=ResponseModel[Token])
|
||||
async def login(
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""用户登录"""
|
||||
# 实现登录逻辑
|
||||
pass
|
||||
|
||||
@router.post("/register", response_model=ResponseModel[Token])
|
||||
async def register(
|
||||
user_data: UserRegister,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""用户注册"""
|
||||
# 实现注册逻辑
|
||||
pass
|
||||
|
||||
@router.post("/logout")
|
||||
async def logout():
|
||||
"""用户登出"""
|
||||
# 实现登出逻辑
|
||||
pass
|
||||
|
||||
@router.post("/refresh", response_model=ResponseModel[Token])
|
||||
async def refresh_token(refresh_token: str):
|
||||
"""刷新Token"""
|
||||
# 实现Token刷新逻辑
|
||||
pass
|
||||
|
||||
@router.post("/reset-password")
|
||||
async def reset_password(email: str):
|
||||
"""重置密码"""
|
||||
# 实现密码重置逻辑
|
||||
pass
|
||||
```
|
||||
|
||||
### 2. 安全核心功能 (`app/core/security.py`)
|
||||
已在基础代码中部分实现,你需要完善:
|
||||
- 密码加密和验证
|
||||
- JWT Token生成和验证
|
||||
- 权限验证装饰器
|
||||
|
||||
### 3. 认证依赖注入 (`app/core/deps.py` 或 `app/api/deps.py`)
|
||||
```python
|
||||
async def get_current_user(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
token: str = Depends(oauth2_scheme)
|
||||
) -> User:
|
||||
"""获取当前登录用户"""
|
||||
# 实现逻辑
|
||||
pass
|
||||
|
||||
async def require_admin(user: User = Depends(get_current_user)) -> User:
|
||||
"""需要管理员权限"""
|
||||
# 实现逻辑
|
||||
pass
|
||||
```
|
||||
|
||||
### 4. Schema定义 (`app/schemas/auth.py`)
|
||||
```python
|
||||
from pydantic import EmailStr, Field
|
||||
from app.schemas.base import BaseSchema
|
||||
|
||||
class UserLogin(BaseSchema):
|
||||
username: str = Field(..., description="用户名或邮箱")
|
||||
password: str = Field(..., description="密码")
|
||||
|
||||
class UserRegister(BaseSchema):
|
||||
username: str = Field(..., min_length=3, max_length=20)
|
||||
email: EmailStr
|
||||
password: str = Field(..., min_length=8)
|
||||
confirm_password: str
|
||||
|
||||
class Token(BaseSchema):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
expires_in: int
|
||||
```
|
||||
|
||||
### 5. 认证服务 (`app/services/auth_service.py`)
|
||||
```python
|
||||
from app.services.base_service import BaseService
|
||||
|
||||
class AuthService:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.db = db
|
||||
|
||||
async def authenticate_user(self, username: str, password: str):
|
||||
"""验证用户身份"""
|
||||
pass
|
||||
|
||||
async def create_user(self, user_data: UserRegister):
|
||||
"""创建新用户"""
|
||||
pass
|
||||
|
||||
async def create_tokens(self, user_id: int):
|
||||
"""创建访问令牌和刷新令牌"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 6. 测试用例 (`tests/unit/test_auth.py`)
|
||||
```python
|
||||
import pytest
|
||||
from app.services.auth_service import AuthService
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_registration(db_session):
|
||||
"""测试用户注册"""
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_login(db_session):
|
||||
"""测试用户登录"""
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_token_refresh(db_session):
|
||||
"""测试Token刷新"""
|
||||
pass
|
||||
```
|
||||
|
||||
## 与其他模块的接口
|
||||
|
||||
### 提供给其他模块的功能
|
||||
1. `get_current_user` - 获取当前登录用户
|
||||
2. `require_admin` - 需要管理员权限
|
||||
3. `require_manager` - 需要管理者权限
|
||||
4. `create_access_token` - 创建访问令牌
|
||||
5. `verify_password` - 验证密码
|
||||
6. `get_password_hash` - 获取密码哈希
|
||||
|
||||
### API端点
|
||||
- POST `/api/v1/auth/login` - 用户登录
|
||||
- POST `/api/v1/auth/register` - 用户注册
|
||||
- POST `/api/v1/auth/logout` - 用户登出
|
||||
- POST `/api/v1/auth/refresh` - 刷新Token
|
||||
- POST `/api/v1/auth/reset-password` - 重置密码
|
||||
|
||||
## 安全要求
|
||||
1. 密码必须使用bcrypt加密存储
|
||||
2. JWT Token必须设置合理的过期时间
|
||||
3. 刷新Token必须与访问Token分开存储
|
||||
4. 实现登录失败次数限制
|
||||
5. 敏感操作需要二次验证
|
||||
|
||||
## 性能要求
|
||||
1. 登录响应时间 < 200ms
|
||||
2. Token验证时间 < 50ms
|
||||
3. 合理使用Redis缓存Token黑名单
|
||||
|
||||
## 参考资源
|
||||
- FastAPI Security文档: https://fastapi.tiangolo.com/tutorial/security/
|
||||
- JWT最佳实践: https://tools.ietf.org/html/rfc8725
|
||||
- OWASP认证指南: https://owasp.org/www-project-cheat-sheets/
|
||||
Reference in New Issue
Block a user