16 KiB
16 KiB
认证接口
**本文档引用的文件** - [backend/app/api/auth.py](file://backend/app/api/auth.py) - [backend/app/schemas/auth.py](file://backend/app/schemas/auth.py) - [backend/app/services/auth.py](file://backend/app/services/auth.py) - [backend/app/models/user.py](file://backend/app/models/user.py) - [backend/app/api/deps.py](file://backend/app/api/deps.py) - [backend/app/config.py](file://backend/app/config.py) - [backend/app/main.py](file://backend/app/main.py) - [backend/app/database.py](file://backend/app/database.py) - [tests/test_auth.py](file://tests/test_auth.py) - [backend/requirements.txt](file://backend/requirements.txt)目录
简介
本文件为认证系统的完整API文档,覆盖以下接口:
- 用户注册:/api/v1/auth/register
- 用户登录:/api/v1/auth/login
- 获取当前用户:/api/v1/auth/me
文档详细说明每个接口的请求参数、响应格式、状态码、错误处理以及JWT令牌生成机制、认证流程与安全考虑,并提供认证中间件使用指南与最佳实践。
项目结构
后端采用FastAPI + SQLAlchemy异步ORM + PostgreSQL + Redis的架构,认证模块位于backend/app/api/auth.py,配合服务层、模型层与依赖注入实现。
graph TB
subgraph "后端应用"
A["FastAPI 应用<br/>路由注册"]
B["认证路由<br/>/api/v1/auth/*"]
C["认证服务<br/>密码哈希/校验/JWT"]
D["数据库会话<br/>AsyncSession"]
E["用户模型<br/>SQLAlchemy ORM"]
F["认证依赖<br/>OAuth2PasswordBearer"]
end
A --> B
B --> C
B --> D
C --> D
D --> E
F --> C
F --> D
图表来源
- backend/app/main.py:38
- backend/app/api/auth.py:10
- backend/app/services/auth.py:13
- backend/app/database.py:23
- backend/app/models/user.py:11
- backend/app/api/deps.py:13
章节来源
核心组件
- 路由器:在主应用中注册认证路由前缀为/api/v1/auth
- 认证服务:提供密码哈希/校验、JWT生成/校验、用户注册与认证
- 数据模型:用户表结构,包含邮箱、密码哈希、计划等级、配额等字段
- 依赖注入:OAuth2PasswordBearer用于从Authorization头提取Bearer令牌,get_current_user解析并验证JWT,加载当前用户
- 配置:JWT密钥与过期时间、数据库连接等
章节来源
- backend/app/api/auth.py:10
- backend/app/services/auth.py:13
- backend/app/models/user.py:11
- backend/app/api/deps.py:13
- backend/app/config.py:9
架构总览
认证系统遵循“请求-服务-模型-数据库”的分层设计,使用OAuth2 Bearer令牌进行无状态认证。
sequenceDiagram
participant 客户端 as "客户端"
participant 路由 as "认证路由"
participant 服务 as "认证服务"
participant 数据库 as "数据库"
participant 依赖 as "认证依赖"
客户端->>路由 : POST /api/v1/auth/register
路由->>服务 : 注册用户(邮箱/密码/姓名)
服务->>数据库 : 查询邮箱是否已存在
数据库-->>服务 : 结果
服务->>服务 : 哈希密码
服务->>数据库 : 创建用户记录
数据库-->>服务 : 新用户对象
服务-->>路由 : 返回UserResponse
路由-->>客户端 : 201 Created + UserResponse
客户端->>路由 : POST /api/v1/auth/login
路由->>服务 : 认证(邮箱/密码)
服务->>数据库 : 查询用户
数据库-->>服务 : 用户或None
服务->>服务 : 校验密码
服务->>服务 : 生成JWT(access_token)
服务-->>路由 : 返回TokenResponse
路由-->>客户端 : 200 OK + TokenResponse
客户端->>依赖 : GET /api/v1/auth/me (Authorization : Bearer)
依赖->>依赖 : 解析并验证JWT
依赖->>数据库 : 按ID查询用户
数据库-->>依赖 : 用户对象
依赖-->>客户端 : 200 OK + UserResponse
图表来源
- backend/app/api/auth.py:13
- backend/app/api/auth.py:22
- backend/app/api/auth.py:40
- backend/app/services/auth.py:37
- backend/app/services/auth.py:55
- backend/app/api/deps.py:16
详细组件分析
用户注册接口 /api/v1/auth/register
- 方法与路径:POST /api/v1/auth/register
- 请求体模型:UserRegister
- 字段:email(邮箱)、password(字符串,最小长度8)、name(字符串,1-100)
- 响应模型:UserResponse
- 字段:id(UUID)、email(字符串)、name(可空)、plan(字符串,默认"free")、max_queries(整数,默认5)、is_active(布尔,默认true)、created_at(时间戳)
- 成功响应:201 Created
- 错误响应:
- 400 Bad Request:当邮箱已被注册时,返回错误详情
- 处理流程:
- 调用注册服务,检查邮箱唯一性
- 对密码进行哈希处理
- 写入数据库并返回新用户信息
flowchart TD
Start(["请求进入"]) --> Validate["校验请求体<br/>邮箱/密码/姓名"]
Validate --> CheckDup{"邮箱已存在?"}
CheckDup --> |是| Return400["返回400错误<br/>邮箱已注册"]
CheckDup --> |否| HashPwd["哈希密码"]
HashPwd --> Create["创建用户记录"]
Create --> Commit["提交事务"]
Commit --> Refresh["刷新用户对象"]
Refresh --> Return201["返回201 + UserResponse"]
图表来源
- backend/app/api/auth.py:13
- backend/app/services/auth.py:37
- backend/app/schemas/auth.py:7
- backend/app/schemas/auth.py:18
章节来源
- backend/app/api/auth.py:13
- backend/app/schemas/auth.py:7
- backend/app/schemas/auth.py:18
- backend/app/services/auth.py:37
- tests/test_auth.py:26
- tests/test_auth.py:43
请求示例
- POST /api/v1/auth/register
- 请求体JSON:
- email: "string"
- password: "string(≥8)"
- name: "string(1-100)"
响应示例
- 201 Created
- 响应体JSON:
- id: "uuid"
- email: "string"
- name: "string|null"
- plan: "string"
- max_queries: integer
- is_active: boolean
- created_at: "datetime"
状态码
- 201 Created:注册成功
- 400 Bad Request:邮箱已注册
错误处理
- 当服务层抛出ValueError(如邮箱重复),路由捕获并返回400
用户登录接口 /api/v1/auth/login
- 方法与路径:POST /api/v1/auth/login
- 请求体模型:UserLogin
- 字段:email(邮箱)、password(字符串)
- 响应模型:TokenResponse
- 字段:access_token(字符串)、token_type(字符串,固定为"bearer")、user(UserResponse)
- 成功响应:200 OK
- 错误响应:
- 401 Unauthorized:邮箱或密码不正确,携带WWW-Authenticate: Bearer头
- 处理流程:
- 使用邮箱查询用户
- 校验密码哈希
- 生成JWT令牌(含过期时间),返回access_token与用户信息
sequenceDiagram
participant 客户端 as "客户端"
participant 路由 as "登录路由"
participant 服务 as "认证服务"
participant 数据库 as "数据库"
客户端->>路由 : POST /api/v1/auth/login
路由->>服务 : authenticate_user(邮箱, 密码)
服务->>数据库 : 查询用户
数据库-->>服务 : 用户或None
服务->>服务 : 校验密码
alt 用户不存在或密码错误
服务-->>路由 : None
路由-->>客户端 : 401 Unauthorized
else 登录成功
服务-->>路由 : User对象
路由->>服务 : create_access_token({sub : userId})
服务-->>路由 : access_token
路由-->>客户端 : 200 OK + TokenResponse
end
图表来源
- backend/app/api/auth.py:22
- backend/app/api/auth.py:24
- backend/app/services/auth.py:55
- backend/app/services/auth.py:24
章节来源
- backend/app/api/auth.py:22
- backend/app/api/auth.py:24
- backend/app/schemas/auth.py:13
- backend/app/schemas/auth.py:30
- backend/app/services/auth.py:55
- backend/app/services/auth.py:24
- tests/test_auth.py:62
- tests/test_auth.py:76-84
请求示例
- POST /api/v1/auth/login
- 请求体JSON:
- email: "string"
- password: "string"
响应示例
- 200 OK
- 响应体JSON:
- access_token: "string"
- token_type: "bearer"
- user: { id: "uuid" email: "string" name: "string|null" plan: "string" max_queries: integer is_active: boolean created_at: "datetime" }
状态码
- 200 OK:登录成功
- 401 Unauthorized:邮箱或密码不正确
安全考虑
- 密码使用BCrypt哈希存储
- JWT使用HS256算法与密钥签名,过期时间由配置控制
- 登录失败返回统一错误消息,避免泄露账户存在性细节
获取当前用户接口 /api/v1/auth/me
- 方法与路径:GET /api/v1/auth/me
- 权限要求:需要Bearer令牌(Authorization: Bearer )
- 依赖注入:get_current_user
- 通过OAuth2PasswordBearer从Authorization头提取令牌
- 校验JWT并解析sub(用户ID)
- 从数据库按ID查询用户并返回
- 响应模型:UserResponse
- 成功响应:200 OK
- 错误响应:
- 401 Unauthorized:令牌无效、过期或用户不存在
sequenceDiagram
participant 客户端 as "客户端"
participant 依赖 as "get_current_user"
participant 服务 as "verify_token"
participant 数据库 as "数据库"
客户端->>依赖 : GET /api/v1/auth/me (Authorization : Bearer)
依赖->>依赖 : 从Authorization头提取token
依赖->>服务 : verify_token(token)
服务-->>依赖 : payload(sub=userId)
依赖->>数据库 : 查询用户by id
数据库-->>依赖 : 用户或None
alt 令牌无效/用户不存在
依赖-->>客户端 : 401 Unauthorized
else 成功
依赖-->>客户端 : 200 OK + UserResponse
end
图表来源
- backend/app/api/deps.py:16
- backend/app/api/deps.py:27
- backend/app/services/auth.py:32
- backend/app/api/auth.py:40
章节来源
- backend/app/api/auth.py:40
- backend/app/api/deps.py:16
- backend/app/api/deps.py:27
- backend/app/services/auth.py:32
- tests/test_auth.py:88
- tests/test_auth.py:99
请求示例
- GET /api/v1/auth/me
- 请求头:
- Authorization: "Bearer <access_token>"
响应示例
- 200 OK
- 响应体JSON:UserResponse
状态码
- 200 OK:成功获取当前用户
- 401 Unauthorized:未提供有效令牌或令牌无效
依赖分析
- 外部库依赖:FastAPI、SQLAlchemy异步、Pydantic、python-jose、passlib、redis、apscheduler、playwright等
- 认证相关依赖:OAuth2PasswordBearer、JWT编码/解码、BCrypt密码哈希
- 配置项:JWT_SECRET、JWT_EXPIRE_HOURS、DATABASE_URL、REDIS_URL
graph LR
A["FastAPI 应用"] --> B["认证路由"]
B --> C["认证服务"]
C --> D["Passlib(Bcrypt)"]
C --> E["python-jose(JWT)"]
C --> F["SQLAlchemy 异步"]
F --> G["PostgreSQL"]
A --> H["CORS 中间件"]
A --> I["依赖注入容器"]
图表来源
- backend/requirements.txt:2
- backend/requirements.txt:16
- backend/requirements.txt:17
- backend/app/main.py:30
- backend/app/api/deps.py:13
- backend/app/services/auth.py:13
章节来源
- backend/requirements.txt:2
- backend/requirements.txt:16
- backend/requirements.txt:17
- backend/app/main.py:30
- backend/app/api/deps.py:13
- backend/app/services/auth.py:13
性能考虑
- 数据库访问:注册与登录均执行单次查询,使用异步会话减少阻塞
- 密码哈希:BCrypt成本因子默认设置,平衡安全性与性能
- JWT过期:可通过配置调整过期时间,建议根据业务场景权衡
- 缓存策略:当前未对用户信息做缓存,可在高频读取场景引入Redis缓存
故障排除指南
- 注册失败(400):确认邮箱唯一性;检查请求体字段类型与长度
- 登录失败(401):确认邮箱与密码正确;检查JWT密钥与过期时间配置
- 获取当前用户失败(401):确认Authorization头格式为Bearer ;检查令牌是否过期
- 数据库连接问题:检查DATABASE_URL配置;确保PostgreSQL服务可用
- CORS跨域问题:确认前端域名已在CORS允许列表中
章节来源
- tests/test_auth.py:43
- tests/test_auth.py:76-84
- tests/test_auth.py:99
- backend/app/config.py:7
- backend/app/main.py:30
结论
认证系统基于FastAPI与SQLAlchemy异步ORM构建,采用OAuth2 Bearer令牌与JWT实现无状态认证。注册与登录流程清晰,错误处理明确,具备良好的扩展性与安全性基础。建议在生产环境完善令牌刷新策略、速率限制与审计日志,并定期轮换JWT密钥。
附录
JWT令牌生成机制
- 签名算法:HS256
- 过期时间:由配置JWT_EXPIRE_HOURS决定
- 载荷:包含sub(用户ID)与exp(过期时间)
- 生成流程:在登录成功后调用create_access_token(data={"sub": str(user.id)})
章节来源
认证中间件使用指南与最佳实践
- 在需要保护的路由上使用依赖注入:Depends(get_current_user)
- 前端在每次请求中携带Authorization: Bearer <access_token>
- 生产环境务必设置安全的JWT_SECRET并启用HTTPS
- 建议实现令牌刷新与登出机制,避免长期持有高权限令牌
- 对登录失败与敏感操作增加速率限制与日志审计
章节来源