You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

6.4 KiB

统一认证依赖注入

日期: 2026-01-29
类型: 架构优化
影响范围: 认证系统、API 路由

背景

项目中存在两个依赖注入文件,导致职责不清:

  1. app/api/deps.py - API 层依赖注入
  2. app/core/deps.py - 核心层依赖注入

之前的实现中,两个文件都包含了 get_current_userget_current_user_optionalget_redis 等业务相关的依赖注入,导致代码重复和职责混乱。

架构原则

根据项目架构设计:

  • app/core/: 存放全局基础设施,与业务无关的底层能力(如数据库连接、配置管理、日志系统)
  • app/api/deps.py: 存放 API 层的依赖注入,包含业务相关的依赖(如认证、授权、Redis 客户端)

解决方案

1. 明确职责分离

app/api/deps.py - API 层依赖注入(业务相关):

  • get_current_user - 获取当前登录用户(必需)
  • get_current_user_optional - 获取当前登录用户(可选)
  • get_redis - 获取 Redis 客户端
  • 其他 API 层需要的依赖注入

app/core/deps.py - 已删除:

  • 由于当前没有基础设施相关的依赖注入需求,文件已删除
  • 未来如需添加(如全局日志、监控等),可重新创建

2. 统一导入路径

所有 API 路由统一从 app.api.deps 导入业务相关的依赖:

from app.api.deps import get_current_user, get_redis

变更内容

1. 删除 app/core/deps.py

由于所有业务相关的依赖注入已迁移到 app/api/deps.pyapp/core/deps.py 文件已无实际用途,因此删除:

  • 删除整个文件
  • 未来如需添加基础设施相关的依赖注入,可重新创建此文件

2. 保留 app/api/deps.py

包含所有 API 层需要的依赖注入:

  • get_current_user - 认证依赖
  • get_current_user_optional - 可选认证依赖
  • get_redis - Redis 客户端依赖
  • security - HTTPBearer 认证方案

3. 更新所有 API 路由导入

将以下文件的导入从 app.core.deps 改为 app.api.deps

  • server/app/api/v1/users.py
  • server/app/api/v1/auth.py
  • server/app/api/v1/folders.py
  • server/app/api/v1/projects.py
  • server/app/api/v1/attachments.py
  • server/app/api/v1/credits.py
  • server/app/api/v1/recharge.py
  • server/app/api/v1/wechat.py
  • server/app/api/v1/ai.py
  • server/app/api/v1/file_storage.py
  • server/tests/integration/test_credit_api.py

技术细节

认证依赖注入实现

# app/api/deps.py

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlmodel.ext.asyncio.session import AsyncSession

from app.core.database import get_session
from app.services.user_service import UserService
from app.models.user import User

security = HTTPBearer()


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    session: AsyncSession = Depends(get_session)
) -> User:
    """
    获取当前登录用户
    
    从请求头的 Authorization Bearer Token 中解析用户信息
    """
    token = credentials.credentials
    
    service = UserService(session)
    user = await service.verify_token(token)
    
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的认证凭证或 Token 已过期",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    return user


async def get_current_user_optional(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False)),
    session: AsyncSession = Depends(get_session)
) -> Optional[User]:
    """
    获取当前登录用户(可选)
    
    如果请求头中没有 Token,返回 None 而不是抛出异常
    """
    if not credentials:
        return None
    
    token = credentials.credentials
    
    service = UserService(session)
    user = await service.verify_token(token)
    
    return user


async def get_redis() -> AsyncGenerator[Redis, None]:
    """获取 Redis 客户端"""
    redis = Redis.from_url(
        settings.REDIS_URL,
        encoding="utf-8",
        decode_responses=True
    )
    try:
        yield redis
    finally:
        await redis.close()

使用示例

# API 路由中使用
from app.api.deps import get_current_user, get_redis

@router.get("/example")
async def example(
    current_user: User = Depends(get_current_user),
    redis: Redis = Depends(get_redis)
):
    # 业务逻辑
    pass

影响范围

API 路由

所有需要认证的 API 端点现在统一从 app.api.deps 导入:

  • 用户管理 (/api/v1/users)
  • 认证 (/api/v1/auth)
  • 文件夹 (/api/v1/folders)
  • 项目 (/api/v1/projects)
  • 附件 (/api/v1/attachments)
  • 积分 (/api/v1/credits)
  • 充值 (/api/v1/recharge)
  • 微信 (/api/v1/wechat)
  • AI 服务 (/api/v1/ai)
  • 文件存储 (/api/v1/file-storage)

测试

  • 更新集成测试导入路径
  • 所有测试通过

验证

1. 导入检查

# 确认所有 API 路由都从 app.api.deps 导入
grep -r "from app.api.deps import" server/app/api/v1/

# 确认 app.core.deps 已删除
ls server/app/core/deps.py 2>/dev/null || echo "✅ 文件已删除"

2. 功能测试

# 运行集成测试
docker exec jointo-server-app pytest tests/integration/ -v

所有认证相关的测试应该通过。

3. API 测试

使用 Postman 或 curl 测试需要认证的端点:

# 获取 Token
curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"phone": "13800138000", "code": "123456"}'

# 使用 Token 访问受保护端点
curl -X GET http://localhost:8000/api/v1/users/me \
  -H "Authorization: Bearer <token>"

架构优势

  1. 职责清晰: app/core/ 专注基础设施,app/api/ 专注业务逻辑
  2. 易于维护: 业务相关的依赖集中在 app/api/deps.py
  3. 符合分层架构: 核心层不依赖业务层
  4. 便于扩展: 未来添加新的 API 依赖时,直接在 app/api/deps.py 中添加

相关文档