# 认证系统

<cite>
**本文档引用的文件**
- [backend/app/models/user.py](file://backend/app/models/user.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/api/auth.py](file://backend/app/api/auth.py)
- [backend/app/api/deps.py](file://backend/app/api/deps.py)
- [backend/app/config.py](file://backend/app/config.py)
- [backend/app/database.py](file://backend/app/database.py)
- [backend/app/main.py](file://backend/app/main.py)
- [backend/alembic/versions/488d0bd5ab01_initial_migration.py](file://backend/alembic/versions/488d0bd5ab01_initial_migration.py)
- [backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py](file://backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py)
- [frontend/lib/auth.ts](file://frontend/lib/auth.ts)
- [frontend/lib/api.ts](file://frontend/lib/api.ts)
- [frontend/app/(auth)/login/page.tsx](file://frontend/app/(auth)/login/page.tsx)
- [frontend/app/(auth)/register/page.tsx](file://frontend/app/(auth)/register/page.tsx)
- [frontend/app/(auth)/forgot-password/page.tsx](file://frontend/app/(auth)/forgot-password/page.tsx)
- [frontend/app/(auth)/reset-password/page.tsx](file://frontend/app/(auth)/reset-password/page.tsx)
- [frontend/app/(auth)/verify-email/page.tsx](file://frontend/app/(auth)/verify-email/page.tsx)
- [frontend/app/api/auth/[...nextauth]/route.ts](file://frontend/app/api/auth/[...nextauth]/route.ts)
- [tests/test_auth.py](file://tests/test_auth.py)
</cite>

## 更新摘要
**变更内容**
- 新增完整的邮箱验证系统（忘记密码、重置密码、邮箱验证）
- 新增密码重置令牌管理功能
- 新增用户资料修改功能（姓名、头像）
- 新增密码更改功能（旧密码验证）
- 新增数据库字段支持邮箱验证和密码重置
- 前端新增对应的认证页面和API接口

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件为 GEO 平台认证系统的实现文档，覆盖用户模型设计、JWT 令牌生成与验证、用户注册与登录流程、邮箱验证系统、密码重置机制、用户资料修改功能、依赖注入在认证中的应用（当前用户获取与权限检查）、安全最佳实践、令牌存储策略与会话管理，以及认证错误处理与调试指南。系统采用后端 FastAPI + SQLAlchemy 异步 ORM + PostgreSQL + Redis（配置项存在）；前端使用 NextAuth.js 作为会话与令牌桥接层，通过自定义凭据提供者对接后端认证接口。

## 项目结构
后端认证相关模块组织如下：
- 模型层：用户模型定义与数据库表结构，包含邮箱验证和密码重置相关字段
- 模式层：Pydantic 数据校验模型（注册、登录、邮箱验证、密码重置、资料修改等）
- 服务层：密码哈希/校验、JWT 编码/解码、注册与认证业务逻辑、邮箱验证和密码重置服务
- API 层：认证路由（注册、登录、当前用户、邮箱验证、密码重置、资料修改等）、依赖注入解析当前用户
- 配置与数据库：设置加载、异步数据库引擎与会话工厂
- 前端：NextAuth 配置、API 客户端、登录/注册/忘记密码/重置密码/邮箱验证页面

```mermaid
graph TB
subgraph "后端"
M["models/user.py<br/>用户模型<br/>新增邮箱验证字段"]
S["schemas/auth.py<br/>Pydantic 模型<br/>新增邮箱验证Schema"]
SV["services/auth.py<br/>密码/JWT/业务<br/>新增邮箱验证服务"]
A["api/auth.py<br/>认证路由<br/>新增邮箱验证接口"]
D["api/deps.py<br/>依赖注入解析当前用户"]
CFG["config.py<br/>配置"]
DB["database.py<br/>异步数据库"]
MAIN["main.py<br/>应用入口"]
MIG1["alembic 迁移<br/>initial_migration.py"]
MIG2["alembic 迁移<br/>add_user_management_fields.py"]
end
subgraph "前端"
FA["lib/api.ts<br/>API 客户端<br/>新增邮箱验证接口"]
FN["lib/auth.ts<br/>NextAuth 配置"]
LR["app/(auth)/login/page.tsx<br/>登录页"]
RR["app/(auth)/register/page.tsx<br/>注册页"]
FP["app/(auth)/forgot-password/page.tsx<br/>忘记密码页"]
RP["app/(auth)/reset-password/page.tsx<br/>重置密码页"]
VE["app/(auth)/verify-email/page.tsx<br/>邮箱验证页"]
NAR["app/api/auth/[...nextauth]/route.ts<br/>NextAuth 路由"]
end
LR --> FA
RR --> FA
FP --> FA
RP --> FA
VE --> FA
FA --> A
A --> SV
SV --> DB
D --> SV
D --> DB
MAIN --> A
MAIN --> D
CFG --> SV
CFG --> DB
MIG1 --> DB
MIG2 --> DB
FN --> NAR
FA --> FN
```

**图表来源**
- [backend/app/models/user.py:11-48](file://backend/app/models/user.py#L11-L48)
- [backend/app/schemas/auth.py:8-62](file://backend/app/schemas/auth.py#L8-L62)
- [backend/app/services/auth.py:74-175](file://backend/app/services/auth.py#L74-L175)
- [backend/app/api/auth.py:65-115](file://backend/app/api/auth.py#L65-L115)
- [backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py:21-41](file://backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py#L21-L41)
- [frontend/lib/api.ts:55-84](file://frontend/lib/api.ts#L55-L84)
- [frontend/app/(auth)/forgot-password/page.tsx:18-101](file://frontend/app/(auth)/forgot-password/page.tsx#L18-L101)
- [frontend/app/(auth)/reset-password/page.tsx:19-148](file://frontend/app/(auth)/reset-password/page.tsx#L19-L148)
- [frontend/app/(auth)/verify-email/page.tsx:19-155](file://frontend/app/(auth)/verify-email/page.tsx#L19-L155)

**章节来源**
- [backend/app/main.py:24-48](file://backend/app/main.py#L24-L48)
- [backend/app/api/auth.py:65-115](file://backend/app/api/auth.py#L65-L115)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
- [backend/app/services/auth.py:74-175](file://backend/app/services/auth.py#L74-L175)
- [backend/app/schemas/auth.py:8-62](file://backend/app/schemas/auth.py#L8-L62)
- [backend/app/models/user.py:11-48](file://backend/app/models/user.py#L11-L48)
- [backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py:21-41](file://backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py#L21-L41)
- [frontend/lib/api.ts:55-84](file://frontend/lib/api.ts#L55-L84)
- [frontend/app/(auth)/forgot-password/page.tsx:18-101](file://frontend/app/(auth)/forgot-password/page.tsx#L18-L101)
- [frontend/app/(auth)/reset-password/page.tsx:19-148](file://frontend/app/(auth)/reset-password/page.tsx#L19-L148)
- [frontend/app/(auth)/verify-email/page.tsx:19-155](file://frontend/app/(auth)/verify-email/page.tsx#L19-L155)

## 核心组件
- 用户模型与数据库表
  - 字段与约束：UUID 主键、唯一邮箱、密码哈希、可选姓名、计划与配额、激活状态、邮箱验证状态、验证码及过期时间、密码重置令牌及过期时间、头像URL、管理员权限、时间戳等
  - 关系：与查询与订阅的一对多级联删除
- Pydantic 模式
  - 注册：邮箱、密码最小长度、姓名长度限制
  - 登录：邮箱与密码
  - 忘记密码：邮箱
  - 重置密码：令牌与新密码
  - 邮箱验证：邮箱与6位验证码
  - 密码更改：旧密码与新密码
  - 资料修改：可选姓名与头像URL
  - 响应：用户信息与创建时间
- 服务层
  - 密码：bcrypt 上下文进行哈希与校验
  - JWT：HS256 签名，基于配置的密钥与过期时长
  - 注册：去重检查、哈希密码、持久化
  - 认证：邮箱查找、密码校验
  - 邮箱验证：验证码生成与校验、重置令牌生成与校验
  - 资料修改：用户名与头像更新
- API 层
  - 注册：接收注册体，调用服务，异常转 HTTP
  - 登录：认证失败返回未授权，成功签发访问令牌
  - 当前用户：通过依赖注入解析当前用户
  - 邮箱验证：发送验证码、验证验证码、重新发送验证码
  - 密码重置：发送重置链接、重置密码、验证令牌
  - 资料修改：更新用户资料
- 依赖注入
  - OAuth2 密码流解析 Bearer 令牌，解码 JWT，按 ID 查询用户
- 配置与数据库
  - 设置：数据库 URL、Redis URL、JWT 密钥、过期小时数等
  - 引擎：异步 PostgreSQL 引擎与会话工厂
- 前端集成
  - NextAuth 使用凭据提供者对接后端登录接口，以 JWT 策略存储会话
  - 新增忘记密码、重置密码、邮箱验证页面

**章节来源**
- [backend/app/models/user.py:11-48](file://backend/app/models/user.py#L11-L48)
- [backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py:21-41](file://backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py#L21-L41)
- [backend/app/schemas/auth.py:8-62](file://backend/app/schemas/auth.py#L8-L62)
- [backend/app/services/auth.py:74-175](file://backend/app/services/auth.py#L74-L175)
- [backend/app/api/auth.py:65-115](file://backend/app/api/auth.py#L65-L115)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
- [backend/app/config.py:4-17](file://backend/app/config.py#L4-L17)
- [backend/app/database.py:6-29](file://backend/app/database.py#L6-L29)
- [frontend/lib/auth.ts:5-56](file://frontend/lib/auth.ts#L5-L56)
- [frontend/lib/api.ts:55-84](file://frontend/lib/api.ts#L55-L84)

## 架构总览
后端认证流程概览：
- 前端登录/注册/忘记密码/重置密码/邮箱验证页面通过 API 客户端调用后端 /api/v1/auth/* 接口
- 后端服务层完成密码哈希/校验、JWT 签发、邮箱验证与密码重置服务
- 依赖注入层解析 Bearer 令牌并解析当前用户
- 前端 NextAuth 将后端返回的访问令牌存入本地会话（JWT 策略）

```mermaid
sequenceDiagram
participant FE as "前端页面"
participant API as "API 客户端"
participant AUTH as "后端认证路由"
participant SVC as "认证服务"
participant DB as "数据库"
participant DEP as "依赖注入"
FE->>API : "提交登录/注册/忘记密码/重置密码/邮箱验证表单"
API->>AUTH : "POST /api/v1/auth/login/register/forgot-password/reset-password/verify-email"
AUTH->>SVC : "authenticate_user/register_user/send_reset_link/verify_email/change_password/update_profile"
SVC->>DB : "查询/插入/更新用户"
DB-->>SVC : "返回用户/结果"
SVC-->>AUTH : "返回用户或令牌"
AUTH-->>API : "返回 {access_token, user} 或操作结果"
API-->>FE : "保存令牌并跳转"
FE->>DEP : "后续请求携带 Authorization : Bearer"
DEP->>SVC : "verify_token 解码"
SVC-->>DEP : "payload(sub)"
DEP->>DB : "按ID查询用户"
DB-->>DEP : "返回用户"
DEP-->>FE : "注入当前用户"
```

**图表来源**
- [frontend/lib/api.ts:55-84](file://frontend/lib/api.ts#L55-L84)
- [backend/app/api/auth.py:65-115](file://backend/app/api/auth.py#L65-L115)
- [backend/app/services/auth.py:74-175](file://backend/app/services/auth.py#L74-L175)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
- [frontend/lib/auth.ts:13-32](file://frontend/lib/auth.ts#L13-L32)

## 详细组件分析

### 用户模型与数据库表
- 字段与类型
  - id: UUID 主键，默认生成
  - email: 字符串，唯一且非空
  - password_hash: 字符串，非空
  - name: 字符串，可空
  - plan: 字符串，默认 "free"
  - max_queries: 整数，默认 5
  - is_active: 布尔，默认 true
  - email_verified: 布尔，默认 false（新增）
  - verification_code: 字符串，长度6，可空（新增）
  - verification_code_expires: 时间戳，可空（新增）
  - reset_token: 字符串，可空（新增）
  - reset_token_expires: 时间戳，可空（新增）
  - avatar_url: 字符串，可空（新增）
  - is_admin: 布尔，默认 false（新增）
  - created_at/updated_at: 时间戳，默认当前时间，更新时同步
- 约束与索引
  - 唯一约束：email
  - 外键：查询与订阅表关联 users(id)，级联删除
- 关系
  - 一对多：用户 -> 查询、订阅

```mermaid
erDiagram
USERS {
uuid id PK
string email UK
string password_hash
string name
string plan
int max_queries
boolean is_active
boolean email_verified
string verification_code
timestamp verification_code_expires
string reset_token
timestamp reset_token_expires
string avatar_url
boolean is_admin
timestamp created_at
timestamp updated_at
}
QUERIES {
uuid id PK
uuid user_id FK
string keyword
string target_brand
jsonb brand_aliases
jsonb platforms
string frequency
string status
timestamp last_queried_at
timestamp next_query_at
timestamp created_at
timestamp updated_at
}
SUBSCRIPTIONS {
uuid id PK
uuid user_id FK
string plan
string status
date start_date
date end_date
numeric amount
string payment_method
string payment_id
timestamp created_at
}
USERS ||--o{ QUERIES : "拥有"
USERS ||--o{ SUBSCRIPTIONS : "拥有"
```

**图表来源**
- [backend/app/models/user.py:11-48](file://backend/app/models/user.py#L11-L48)
- [backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py:21-41](file://backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py#L21-L41)

**章节来源**
- [backend/app/models/user.py:11-48](file://backend/app/models/user.py#L11-L48)
- [backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py:21-41](file://backend/alembic/versions/c3d5e7f9ab12_add_user_management_fields.py#L21-L41)

### JWT 令牌生成与验证机制
- 签名算法：HS256
- 密钥来源：配置项 JWT_SECRET
- 过期时间：配置项 JWT_EXPIRE_HOURS（小时）
- 生成流程
  - 以用户 ID 作为 sub，加入过期时间，使用 HS256 签名编码
- 验证流程
  - 使用相同密钥与算法解码，提取 sub（用户 ID），再按 ID 查询用户
- 刷新策略
  - 代码中未实现刷新令牌机制；当前为一次性访问令牌，过期后需重新登录

```mermaid
flowchart TD
Start(["开始"]) --> Encode["构造载荷{sub, exp}<br/>HS256 签名"]
Encode --> Token["生成 access_token"]
Token --> Verify["解码 access_token<br/>校验算法与密钥"]
Verify --> ParseSub["提取 sub(用户ID)"]
ParseSub --> Lookup["按ID查询用户"]
Lookup --> Done(["结束"])
```

**图表来源**
- [backend/app/services/auth.py:27-37](file://backend/app/services/auth.py#L27-L37)
- [backend/app/api/deps.py:26-37](file://backend/app/api/deps.py#L26-L37)
- [backend/app/config.py:9-10](file://backend/app/config.py#L9-L10)

**章节来源**
- [backend/app/services/auth.py:27-37](file://backend/app/services/auth.py#L27-L37)
- [backend/app/api/deps.py:26-37](file://backend/app/api/deps.py#L26-L37)
- [backend/app/config.py:9-10](file://backend/app/config.py#L9-L10)

### 用户注册流程
- 输入校验：邮箱格式、密码最小长度、姓名长度
- 去重检查：按邮箱查询，若已存在则抛出错误
- 密码处理：bcrypt 哈希
- 持久化：创建用户记录并提交事务
- 返回：用户信息（不含敏感字段）

```mermaid
flowchart TD
RStart(["注册入口"]) --> Validate["Pydantic 校验"]
Validate --> CheckDup["按邮箱查重"]
CheckDup --> Dup{"已存在?"}
Dup -- 是 --> Err["抛出错误"]
Dup -- 否 --> Hash["bcrypt 哈希密码"]
Hash --> Persist["创建用户并提交"]
Persist --> RDone(["返回用户信息"])
Err --> RDone
```

**图表来源**
- [backend/app/schemas/auth.py:8-11](file://backend/app/schemas/auth.py#L8-L11)
- [backend/app/services/auth.py:40-55](file://backend/app/services/auth.py#L40-L55)

**章节来源**
- [backend/app/schemas/auth.py:8-11](file://backend/app/schemas/auth.py#L8-L11)
- [backend/app/services/auth.py:40-55](file://backend/app/services/auth.py#L40-L55)
- [tests/test_auth.py:25-58](file://tests/test_auth.py#L25-L58)

### 登录认证过程
- 输入校验：邮箱与密码
- 用户查找：按邮箱查询
- 凭据验证：bcrypt 校验密码哈希
- 会话管理：签发访问令牌（Bearer）
- 权限分配：当前用户解析后，后续路由可依赖注入获取用户

```mermaid
sequenceDiagram
participant C as "客户端"
participant A as "认证路由"
participant S as "认证服务"
participant DB as "数据库"
C->>A : "POST /login {email,password}"
A->>S : "authenticate_user(email,password)"
S->>DB : "select * from users where email=?"
DB-->>S : "用户或空"
S->>S : "bcrypt 校验"
S-->>A : "用户或None"
A-->>C : "{access_token,bearer,user}"
```

**图表来源**
- [backend/app/api/auth.py:42-57](file://backend/app/api/auth.py#L42-L57)
- [backend/app/services/auth.py:58-71](file://backend/app/services/auth.py#L58-L71)

**章节来源**
- [backend/app/api/auth.py:42-57](file://backend/app/api/auth.py#L42-L57)
- [backend/app/services/auth.py:58-71](file://backend/app/services/auth.py#L58-L71)
- [tests/test_auth.py:61-84](file://tests/test_auth.py#L61-L84)

### 邮箱验证系统
- 验证码生成与发送
  - 生成6位随机验证码（100000-999999）
  - 设置验证码过期时间为10分钟
  - 日志输出验证码（模拟邮件发送）
- 验证码校验
  - 检查验证码是否匹配
  - 验证过期时间（当前时间之前视为过期）
  - 成功后设置邮箱为已验证，清除验证码和过期时间
- 重置令牌管理
  - 生成UUID作为重置令牌
  - 设置令牌过期时间为1小时
  - 日志输出重置链接（模拟邮件发送）
- 重置密码流程
  - 验证令牌是否存在且未过期
  - 更新用户密码为新密码的哈希值
  - 清除重置令牌和过期时间

```mermaid
flowchart TD
Start(["邮箱验证入口"]) --> SendCode["生成6位验证码<br/>设置10分钟过期"]
SendCode --> StoreCode["存储验证码和过期时间"]
StoreCode --> LogCode["日志输出验证码"]
LogCode --> VerifyCode["验证验证码"]
VerifyCode --> CheckExpire{"是否过期?"}
CheckExpire -- 是 --> Fail["验证失败"]
CheckExpire -- 否 --> SetVerified["设置邮箱已验证<br/>清除验证码"]
SetVerified --> Success["验证成功"]
Start2(["密码重置入口"]) --> SendLink["生成UUID令牌<br/>设置1小时过期"]
SendLink --> StoreToken["存储重置令牌和过期时间"]
StoreToken --> LogLink["日志输出重置链接"]
LogLink --> ResetPwd["重置密码"]
ResetPwd --> CheckToken{"令牌有效?"}
CheckToken -- 否 --> Fail2["重置失败"]
CheckToken -- 是 --> UpdatePwd["更新密码哈希<br/>清除令牌"]
UpdatePwd --> Success2["重置成功"]
```

**图表来源**
- [backend/app/services/auth.py:74-140](file://backend/app/services/auth.py#L74-L140)

**章节来源**
- [backend/app/services/auth.py:74-140](file://backend/app/services/auth.py#L74-L140)
- [backend/app/api/auth.py:65-90](file://backend/app/api/auth.py#L65-L90)

### 密码重置流程
- 忘记密码
  - 接收邮箱地址
  - 生成重置令牌并存储
  - 发送重置链接到邮箱
- 重置密码
  - 接收令牌和新密码
  - 验证令牌有效性（存在且未过期）
  - 更新用户密码为新密码的哈希值
  - 清除重置令牌

```mermaid
sequenceDiagram
participant U as "用户"
participant F as "忘记密码页面"
participant A as "认证路由"
participant S as "认证服务"
participant DB as "数据库"
U->>F : "输入邮箱"
F->>A : "POST /forgot-password"
A->>S : "send_reset_link(email)"
S->>DB : "生成UUID令牌并存储"
DB-->>S : "存储成功"
S-->>A : "发送重置链接"
A-->>F : "返回成功消息"
U->>A : "POST /reset-password"
A->>S : "reset_password(token, new_password)"
S->>DB : "验证令牌并更新密码"
DB-->>S : "更新成功"
S-->>A : "返回成功"
A-->>U : "返回重置成功"
```

**图表来源**
- [frontend/app/(auth)/forgot-password/page.tsx:24-37](file://frontend/app/(auth)/forgot-password/page.tsx#L24-L37)
- [frontend/app/(auth)/reset-password/page.tsx:41-65](file://frontend/app/(auth)/reset-password/page.tsx#L41-L65)
- [backend/app/api/auth.py:65-76](file://backend/app/api/auth.py#L65-L76)
- [backend/app/services/auth.py:110-140](file://backend/app/services/auth.py#L110-L140)

**章节来源**
- [frontend/app/(auth)/forgot-password/page.tsx:18-101](file://frontend/app/(auth)/forgot-password/page.tsx#L18-L101)
- [frontend/app/(auth)/reset-password/page.tsx:19-148](file://frontend/app/(auth)/reset-password/page.tsx#L19-L148)
- [backend/app/api/auth.py:65-76](file://backend/app/api/auth.py#L65-L76)
- [backend/app/services/auth.py:110-140](file://backend/app/services/auth.py#L110-L140)

### 邮箱验证流程
- 验证码发送
  - 接收邮箱地址
  - 生成6位验证码并存储
  - 发送验证码到邮箱
- 验证码验证
  - 接收邮箱和验证码
  - 验证验证码是否匹配且未过期
  - 成功后设置邮箱为已验证
- 重新发送验证码
  - 支持在验证码过期后重新发送

```mermaid
sequenceDiagram
participant U as "用户"
participant V as "邮箱验证页面"
participant A as "认证路由"
participant S as "认证服务"
participant DB as "数据库
U->>V : "输入邮箱"
V->>A : "POST /resend-verification"
A->>S : "send_verification_code(email)"
S->>DB : "生成验证码并存储"
DB-->>S : "存储成功"
S-->>A : "发送验证码"
A-->>V : "返回成功消息"
U->>V : "输入验证码"
V->>A : "POST /verify-email"
A->>S : "verify_email(email, code)"
S->>DB : "验证验证码"
DB-->>S : "验证结果"
S-->>A : "返回验证结果"
A-->>U : "返回验证结果"
```

**图表来源**
- [frontend/app/(auth)/verify-email/page.tsx:37-69](file://frontend/app/(auth)/verify-email/page.tsx#L37-L69)
- [backend/app/api/auth.py:79-90](file://backend/app/api/auth.py#L79-L90)
- [backend/app/services/auth.py:74-107](file://backend/app/services/auth.py#L74-L107)

**章节来源**
- [frontend/app/(auth)/verify-email/page.tsx:19-155](file://frontend/app/(auth)/verify-email/page.tsx#L19-L155)
- [backend/app/api/auth.py:79-90](file://backend/app/api/auth.py#L79-L90)
- [backend/app/services/auth.py:74-107](file://backend/app/services/auth.py#L74-L107)

### 用户资料修改功能
- 资料更新
  - 支持更新姓名和头像URL
  - 仅更新提供的字段
  - 返回更新后的用户信息
- 密码更改
  - 需要当前用户的认证
  - 验证旧密码正确性
  - 更新为新密码的哈希值

```mermaid
flowchart TD
Start(["资料修改入口"]) --> CheckFields["检查提供的字段"]
CheckFields --> UpdateName{"是否提供姓名?"}
UpdateName -- 是 --> SetName["更新姓名"]
UpdateName -- 否 --> CheckAvatar{"是否提供头像URL?"}
SetName --> CheckAvatar
CheckAvatar -- 是 --> SetAvatar["更新头像URL"]
CheckAvatar -- 否 --> Commit["提交事务"]
SetAvatar --> Commit
Commit --> Refresh["刷新用户信息"]
Refresh --> Done(["返回更新后的用户"])
Start2(["密码更改入口"]) --> VerifyOld["验证旧密码"]
VerifyOld --> CheckOld{"旧密码正确?"}
CheckOld -- 否 --> Fail["更改失败"]
CheckOld -- 是 --> HashNew["哈希新密码"]
HashNew --> UpdatePwd["更新密码"]
UpdatePwd --> Commit2["提交事务"]
Commit2 --> Done2(["返回成功"])
```

**图表来源**
- [backend/app/schemas/auth.py:33-41](file://backend/app/schemas/auth.py#L33-L41)
- [backend/app/services/auth.py:143-175](file://backend/app/services/auth.py#L143-L175)

**章节来源**
- [backend/app/schemas/auth.py:33-41](file://backend/app/schemas/auth.py#L33-L41)
- [backend/app/services/auth.py:143-175](file://backend/app/services/auth.py#L143-L175)
- [backend/app/api/auth.py:93-115](file://backend/app/api/auth.py#L93-L115)

### 依赖注入与当前用户获取
- OAuth2 密码流：tokenUrl 指向 /api/v1/auth/login
- 依赖函数 get_current_user：
  - 从 Authorization 头解析 Bearer 令牌
  - 调用 verify_token 解码
  - 提取 sub（UUID），查询用户
  - 不存在或解码失败返回未授权
- 在路由中使用：Depends(get_current_user) 获取当前用户

```mermaid
flowchart TD
DS(["依赖注入入口"]) --> Parse["OAuth2 解析 Bearer"]
Parse --> Decode["verify_token 解码"]
Decode --> Sub{"sub 是否存在?"}
Sub -- 否 --> Unauth["401 未授权"]
Sub -- 是 --> Query["按ID查询用户"]
Query --> Found{"找到用户?"}
Found -- 否 --> Unauth
Found -- 是 --> Inject["注入当前用户"]
Inject --> Done(["下游路由可用"])
Unauth --> Done
```

**图表来源**
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
- [backend/app/services/auth.py:35-37](file://backend/app/services/auth.py#L35-L37)

**章节来源**
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
- [backend/app/api/auth.py:60-62](file://backend/app/api/auth.py#L60-L62)
- [tests/test_auth.py:87-104](file://tests/test_auth.py#L87-L104)

### 前端集成与会话管理
- NextAuth 配置
  - 凭据提供者：向后端发送邮箱/密码
  - authorize：调用后端登录接口，成功则将 access_token 与用户信息写入 JWT
  - 会话策略：jwt
  - 回调：将 access_token 与用户 ID 写入 token/session
- 页面交互
  - 登录页：收集邮箱/密码，调用 signIn("credentials")，错误提示"邮箱或密码错误"
  - 注册页：校验两次密码一致，调用后端注册，随后自动登录
  - 忘记密码页：输入邮箱，调用后端发送重置链接
  - 重置密码页：从URL获取令牌，输入新密码，调用后端重置密码
  - 邮箱验证页：输入验证码，调用后端验证，支持重新发送验证码
- API 客户端
  - 自动在请求头添加 Authorization: Bearer token
  - 统一错误处理与响应解析
  - 新增邮箱验证相关接口

```mermaid
sequenceDiagram
participant L as "登录页"
participant FP as "忘记密码页"
participant RP as "重置密码页"
participant VE as "邮箱验证页"
participant NA as "NextAuth"
participant API as "后端接口"
participant CL as "API 客户端"
L->>NA : "signIn('credentials',{email,password})"
NA->>API : "POST /api/v1/auth/login"
API-->>NA : "{access_token,user}"
NA->>NA : "回调写入 token.accessToken/token.id"
NA-->>L : "登录成功，跳转仪表盘"
L->>CL : "后续请求携带 Authorization : Bearer"
FP->>API : "POST /api/v1/auth/forgot-password"
RP->>API : "POST /api/v1/auth/reset-password"
VE->>API : "POST /api/v1/auth/verify-email"
```

**图表来源**
- [frontend/app/(auth)/login/page.tsx:26-42](file://frontend/app/(auth)/login/page.tsx#L26-L42)
- [frontend/app/(auth)/forgot-password/page.tsx:24-37](file://frontend/app/(auth)/forgot-password/page.tsx#L24-L37)
- [frontend/app/(auth)/reset-password/page.tsx:41-65](file://frontend/app/(auth)/reset-password/page.tsx#L41-L65)
- [frontend/app/(auth)/verify-email/page.tsx:37-69](file://frontend/app/(auth)/verify-email/page.tsx#L37-L69)
- [frontend/lib/auth.ts:13-32](file://frontend/lib/auth.ts#L13-L32)
- [frontend/lib/api.ts:55-84](file://frontend/lib/api.ts#L55-L84)
- [frontend/app/api/auth/[...nextauth]/route.ts:1-6](file://frontend/app/api/auth/[...nextauth]/route.ts#L1-L6)

**章节来源**
- [frontend/lib/auth.ts:5-56](file://frontend/lib/auth.ts#L5-L56)
- [frontend/app/(auth)/login/page.tsx:19-93](file://frontend/app/(auth)/login/page.tsx#L19-L93)
- [frontend/app/(auth)/register/page.tsx:20-128](file://frontend/app/(auth)/register/page.tsx#L20-L128)
- [frontend/app/(auth)/forgot-password/page.tsx:18-101](file://frontend/app/(auth)/forgot-password/page.tsx#L18-L101)
- [frontend/app/(auth)/reset-password/page.tsx:19-148](file://frontend/app/(auth)/reset-password/page.tsx#L19-L148)
- [frontend/app/(auth)/verify-email/page.tsx:19-155](file://frontend/app/(auth)/verify-email/page.tsx#L19-L155)
- [frontend/lib/api.ts:55-84](file://frontend/lib/api.ts#L55-L84)
- [frontend/app/api/auth/[...nextauth]/route.ts:1-6](file://frontend/app/api/auth/[...nextauth]/route.ts#L1-L6)

## 依赖分析
- 组件耦合
  - API 层依赖服务层与数据库依赖注入
  - 服务层依赖配置与密码/JWT工具
  - 依赖注入层依赖服务层的令牌验证与数据库查询
- 外部依赖
  - 数据库：PostgreSQL（异步驱动）
  - 缓存：Redis（配置项存在，当前认证未直接使用）
  - 加密：bcrypt（passlib）
  - JWT：jose（python-jose）
- 可能的循环依赖
  - 当前模块间无明显循环导入

```mermaid
graph LR
API["api/auth.py"] --> SVC["services/auth.py"]
API --> DB["database.py"]
DEP["api/deps.py"] --> SVC
DEP --> DB
SVC --> CFG["config.py"]
SVC --> DB
MAIN["main.py"] --> API
MAIN --> DEP
```

**图表来源**
- [backend/app/api/auth.py:1-115](file://backend/app/api/auth.py#L1-L115)
- [backend/app/api/deps.py:1-43](file://backend/app/api/deps.py#L1-L43)
- [backend/app/services/auth.py:1-175](file://backend/app/services/auth.py#L1-L175)
- [backend/app/database.py:1-29](file://backend/app/database.py#L1-L29)
- [backend/app/config.py:1-17](file://backend/app/config.py#L1-L17)
- [backend/app/main.py:1-48](file://backend/app/main.py#L1-L48)

**章节来源**
- [backend/app/api/auth.py:1-115](file://backend/app/api/auth.py#L1-L115)
- [backend/app/api/deps.py:1-43](file://backend/app/api/deps.py#L1-L43)
- [backend/app/services/auth.py:1-175](file://backend/app/services/auth.py#L1-L175)
- [backend/app/database.py:1-29](file://backend/app/database.py#L1-L29)
- [backend/app/config.py:1-17](file://backend/app/config.py#L1-L17)
- [backend/app/main.py:1-48](file://backend/app/main.py#L1-L48)

## 性能考虑
- 数据库连接
  - 使用异步引擎与会话工厂，减少阻塞
  - 会话配置：关闭自动提交/刷写，expire_on_commit 控制回话生命周期
- 密码哈希
  - bcrypt 默认成本适中，建议在生产环境评估成本参数
- JWT
  - HS256 为对称算法，计算开销低；注意密钥安全与轮换
- 网络与缓存
  - Redis 配置存在但未在认证中使用，可考虑用于令牌黑名单或会话缓存（需额外实现）
- 邮箱验证
  - 验证码和重置令牌都有过期时间控制，避免长期有效的安全风险

## 故障排查指南
- 常见错误与定位
  - 注册重复邮箱：后端抛出值错误，测试断言 400 与错误详情
  - 登录凭据错误：后端返回 401 未授权，测试断言错误详情
  - 未携带或无效令牌：依赖注入解析失败，返回 401
  - 验证码无效或过期：邮箱验证接口返回 400 错误
  - 重置令牌无效或过期：密码重置接口返回 400 错误
  - 旧密码错误：密码更改接口返回 400 错误
- 前端登录失败
  - 检查 NextAuth authorize 是否收到 access_token
  - 确认后端 /api/v1/auth/login 返回结构
- API 请求失败
  - 检查 Authorization 头是否正确附加 Bearer 令牌
  - 统一错误处理会将后端错误消息透传
- 邮箱验证问题
  - 检查验证码是否正确发送到用户邮箱
  - 确认验证码过期时间设置合理
  - 验证重置链接是否正确生成和发送

**章节来源**
- [tests/test_auth.py:25-104](file://tests/test_auth.py#L25-L104)
- [backend/app/api/auth.py:65-115](file://backend/app/api/auth.py#L65-L115)
- [backend/app/api/deps.py:20-41](file://backend/app/api/deps.py#L20-L41)
- [frontend/lib/api.ts:17-39](file://frontend/lib/api.ts#L17-L39)

## 结论
本认证系统采用清晰的分层设计：模式层负责输入校验，服务层封装密码与令牌逻辑、邮箱验证与密码重置服务，API 层提供 REST 接口，依赖注入统一解析当前用户。前端通过 NextAuth 与后端无缝衔接，形成完整的登录/注册与会话管理闭环。新增的邮箱验证系统提供了完整的忘记密码、重置密码和邮箱验证功能，增强了用户体验和安全性。当前实现具备良好的扩展性，建议在生产环境中强化令牌刷新、黑名单与密钥轮换策略，并结合 Redis 实现更完善的会话与令牌治理。

## 附录
- 安全最佳实践
  - 密钥管理：确保 JWT_SECRET 不泄露，定期轮换
  - 传输安全：启用 HTTPS，避免明文传输
  - 输入校验：前端与后端双重校验，防止越界与注入
  - 令牌策略：考虑引入刷新令牌与黑名单机制
  - 会话策略：前端 JWT 策略简单可靠，建议配合 HttpOnly Cookie 与 SameSite 策略进一步加固
  - 邮箱验证：验证码和重置令牌都有过期时间控制，避免长期有效的安全风险
  - 密码安全：密码最小长度限制，使用强哈希算法
- 令牌存储策略
  - 前端：NextAuth JWT 存储在浏览器会话中
  - 后端：当前未实现服务端会话存储，建议结合 Redis 实现
- 权限检查
  - 当前仅实现"当前用户"解析；如需细粒度权限，可在服务层增加角色/资源权限映射并在路由中校验
- 数据库迁移
  - 新增邮箱验证相关字段：email_verified、verification_code、verification_code_expires、reset_token、reset_token_expires、avatar_url、is_admin
  - 所有新增字段都有适当的默认值和约束