Files
backend/.qoder/repowiki/zh/content/API参考/认证API/用户登录.md
lan a4b6c5011e
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
chore(git): 更新.gitignore以忽略新的本地文件
2025-11-30 08:33:17 +08:00

351 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 用户登录
<cite>
**本文引用的文件**
- [auth_handler.go](file://internal/handler/auth_handler.go)
- [routes.go](file://internal/handler/routes.go)
- [jwt.go](file://pkg/auth/jwt.go)
- [password.go](file://pkg/auth/password.go)
- [user_service.go](file://internal/service/user_service.go)
- [common.go](file://internal/types/common.go)
- [response.go](file://internal/model/response.go)
- [user.go](file://internal/model/user.go)
- [token.go](file://internal/model/token.go)
- [token_service.go](file://internal/service/token_service.go)
- [auth.go](file://internal/middleware/auth.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向“用户登录”API围绕 /api/v1/auth/login 端点进行完整说明,覆盖:
- HTTP 方法与路由
- 请求体结构(用户名或邮箱登录)
- 响应格式包含JWT token与用户信息
- 错误码与错误处理
- JWT令牌生成流程访问令牌与声明
- 登录成功/失败后的审计日志记录IP、User-Agent
- 安全建议(防暴力破解、错误提示模糊化)
## 项目结构
该登录流程涉及以下模块协作:
- 路由层:注册 /api/v1/auth/login 路由
- 控制器层:处理登录请求、参数校验、调用服务层
- 服务层:用户查找、密码校验、令牌生成、登录日志记录
- 认证与密码JWT服务、bcrypt密码加解密
- 数据模型:用户、登录日志、令牌
- 中间件JWT认证中间件用于后续受保护接口
```mermaid
graph TB
Client["客户端"] --> Routes["路由注册<br/>/api/v1/auth/login"]
Routes --> Handler["控制器 Login()<br/>参数绑定/错误处理"]
Handler --> Service["服务层 LoginUser()<br/>查找用户/校验密码/生成JWT"]
Service --> JWT["JWT服务 GenerateToken()<br/>签发访问令牌"]
Service --> ModelUser["用户模型 User"]
Service --> ModelLog["登录日志 UserLoginLog"]
Handler --> Resp["统一响应 Response/ErrorResponse"]
Client --> Middleware["认证中间件 AuthMiddleware()<br/>Bearer Token 校验"]
```
图表来源
- [routes.go](file://internal/handler/routes.go#L16-L25)
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [user.go](file://internal/model/user.go#L1-L21)
- [user.go](file://internal/model/user.go#L52-L71)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
章节来源
- [routes.go](file://internal/handler/routes.go#L16-L25)
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
## 核心组件
- 路由与控制器
- /api/v1/auth/login 由控制器 Login 处理负责参数绑定、IP/User-Agent采集、调用服务层并返回统一响应。
- 服务层
- LoginUser 支持用户名或邮箱登录校验用户状态与密码生成JWT更新最后登录时间记录成功/失败日志。
- 认证与密码
- JWT服务提供 GenerateToken/ValidateToken密码使用 bcrypt 进行哈希与校验。
- 数据模型
- User用户信息UserLoginLog登录审计日志Token令牌模型用于后续刷新/失效等场景)。
- 统一响应
- Response/ErrorResponse 提供统一的状态码与消息封装。
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [password.go](file://pkg/auth/password.go#L7-L21)
- [user.go](file://internal/model/user.go#L1-L21)
- [user.go](file://internal/model/user.go#L52-L71)
- [token.go](file://internal/model/token.go#L1-L15)
- [response.go](file://internal/model/response.go#L20-L53)
## 架构总览
登录端到端序列图POST /api/v1/auth/login
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "路由"
participant H as "控制器 Login()"
participant S as "服务层 LoginUser()"
participant J as "JWT服务"
participant U as "用户模型"
participant L as "登录日志"
C->>R : "POST /api/v1/auth/login"
R->>H : "进入 Login 控制器"
H->>H : "ShouldBindJSON 绑定请求体"
H->>S : "LoginUser(jwtService, username, password, ip, ua)"
S->>U : "根据用户名或邮箱查找用户"
S->>S : "校验用户状态"
S->>S : "bcrypt 校验密码"
S->>J : "GenerateToken(userID, username, role)"
J-->>S : "返回JWT字符串"
S->>U : "更新 last_login_at"
S->>L : "logSuccessLogin/logFailedLogin"
S-->>H : "返回 user, token"
H-->>C : "200 成功响应或401失败响应"
```
图表来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [user.go](file://internal/model/user.go#L1-L21)
- [user.go](file://internal/model/user.go#L52-L71)
## 详细组件分析
### 接口定义与请求/响应
- HTTP 方法与路径
- 方法POST
- 路径:/api/v1/auth/login
- 请求体结构
- 字段username支持用户名或邮箱、password6-128字符
- 参数校验:必填、长度约束
- 响应格式
- 成功统一响应data 包含 token 与 userInfo
- 失败:统一错误响应,包含 code 与 message
- 错误码
- 400请求参数错误
- 401未授权用户名/邮箱或密码错误、账号被禁用等)
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [common.go](file://internal/types/common.go#L27-L31)
- [response.go](file://internal/model/response.go#L20-L53)
### 请求体结构与参数校验
- 请求体字段
- username支持用户名或邮箱
- password6-128字符
- 参数校验
- Gin 绑定时会触发结构体 tag 校验(必填、长度范围)
- 实际行为
- 控制器层在绑定失败时直接返回400
章节来源
- [common.go](file://internal/types/common.go#L27-L31)
- [auth_handler.go](file://internal/handler/auth_handler.go#L101-L109)
### 登录流程与JWT生成
- 登录流程要点
- 识别登录方式:包含@视为邮箱,否则为用户名
- 校验用户状态(仅状态为正常才允许登录)
- 使用 bcrypt 校验密码
- 生成JWT访问令牌包含用户ID、用户名、角色等声明
- 更新最后登录时间
- 记录登录日志(成功/失败均记录IP与User-Agent
- JWT声明内容
- 包含 user_id、username、role
- 默认包含过期时间、签发时间、生效时间、签发者等注册声明
- 令牌有效期
- 由JWT服务配置的过期小时数决定
```mermaid
flowchart TD
Start(["进入 LoginUser"]) --> Detect["判断登录方式<br/>用户名或邮箱"]
Detect --> FindUser["查找用户"]
FindUser --> Status{"用户状态正常?"}
Status -- 否 --> LogFail["记录失败日志<br/>原因:账号被禁用"]
LogFail --> ReturnErr["返回错误"]
Status -- 是 --> CheckPwd["bcrypt 校验密码"]
CheckPwd --> PwdOK{"密码正确?"}
PwdOK -- 否 --> LogFail2["记录失败日志<br/>原因:密码错误"]
LogFail2 --> ReturnErr
PwdOK -- 是 --> GenToken["GenerateToken(userID, username, role)"]
GenToken --> UpdateTime["更新 last_login_at"]
UpdateTime --> LogSuccess["记录成功日志"]
LogSuccess --> Done(["返回 user, token"])
ReturnErr --> Done
```
图表来源
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [password.go](file://pkg/auth/password.go#L16-L21)
- [user.go](file://internal/model/user.go#L1-L21)
- [user.go](file://internal/model/user.go#L52-L71)
章节来源
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [password.go](file://pkg/auth/password.go#L16-L21)
### 响应格式与错误码
- 成功响应
- data包含 token 与 userInfoid、username、email、avatar、points、role、status、last_login_at、created_at、updated_at
- 失败响应
- 400请求参数错误
- 401未授权用户名/邮箱或密码错误、账号被禁用)
- 统一响应封装
- Response/ErrorResponse 提供 code、message、data/error 字段
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L131-L147)
- [common.go](file://internal/types/common.go#L107-L126)
- [response.go](file://internal/model/response.go#L20-L53)
### 登录日志记录IP、User-Agent
- 记录内容
- 成功/失败均记录用户ID、IP地址、User-Agent、登录方式PASSWORD、是否成功、失败原因
- 记录时机
- 成功:登录成功后记录
- 失败:用户不存在、账号禁用、密码错误等情况下记录
- 存储位置
- 写入 user_login_logs 表
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L111-L129)
- [user_service.go](file://internal/service/user_service.go#L203-L226)
- [user.go](file://internal/model/user.go#L52-L71)
### 安全考虑与建议
- 防暴力破解
- 建议在网关或应用层增加速率限制例如基于IP或用户名的限流在失败时延长冷却时间或临时封禁
- 可结合验证码机制(当前登录接口未强制验证码,但注册/重置密码有验证码流程)
- 错误提示模糊化
- 当前服务层对“用户不存在/密码错误/账号禁用”均返回统一的“用户名/邮箱或密码错误”,避免泄露具体原因
- 令牌管理
- 当前登录仅返回JWT访问令牌若需刷新令牌可参考后续令牌服务刷新/失效等)能力
- 传输安全
- 建议仅在HTTPS下提供登录接口防止凭据被窃听
章节来源
- [user_service.go](file://internal/service/user_service.go#L88-L103)
- [auth_handler.go](file://internal/handler/auth_handler.go#L116-L129)
## 依赖关系分析
- 控制器依赖
- 控制器 Login 依赖JWT服务、日志、Redis注册流程、服务层 LoginUser
- 服务层依赖
- LoginUser 依赖用户仓库查找用户、JWT服务生成令牌、密码工具bcrypt、登录日志仓库写入日志
- 认证依赖
- JWT服务依赖golang-jwt库密码工具依赖bcrypt
- 模型依赖
- User、UserLoginLog、Token 模型用于数据持久化与审计
```mermaid
graph LR
H["控制器 Login()"] --> S["服务层 LoginUser()"]
H --> J["JWT服务 GenerateToken()"]
S --> U["User 模型"]
S --> L["UserLoginLog 模型"]
S --> P["bcrypt 密码校验"]
H --> R["路由 RegisterRoutes()"]
M["认证中间件 AuthMiddleware()"] --> J
```
图表来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [password.go](file://pkg/auth/password.go#L16-L21)
- [user.go](file://internal/model/user.go#L1-L21)
- [user.go](file://internal/model/user.go#L52-L71)
- [routes.go](file://internal/handler/routes.go#L16-L25)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [user_service.go](file://internal/service/user_service.go#L70-L122)
- [jwt.go](file://pkg/auth/jwt.go#L32-L53)
- [password.go](file://pkg/auth/password.go#L16-L21)
- [user.go](file://internal/model/user.go#L1-L21)
- [user.go](file://internal/model/user.go#L52-L71)
- [routes.go](file://internal/handler/routes.go#L16-L25)
- [auth.go](file://internal/middleware/auth.go#L12-L56)
## 性能考量
- 登录路径涉及数据库查询与密码校验,建议:
- 对用户表建立合适的索引username、email
- 密码校验使用 bcrypt默认成本适中可根据硬件能力调整
- 登录日志写入采用异步或批量策略当前为同步写入注意高并发下的I/O影响
- JWT生成与校验为CPU密集度较低的操作主要瓶颈在数据库与密码校验
## 故障排查指南
- 常见问题与定位
- 400 参数错误:检查请求体字段是否缺失或长度不符合要求
- 401 未授权:核对用户名/邮箱是否存在、账号状态是否正常、密码是否正确
- 登录日志未记录:确认日志写入是否成功,检查数据库连接与表结构
- 日志与审计
- 成功/失败日志均包含 IP 与 User-Agent便于追踪来源设备与来源网络
- 令牌相关
- 若后续引入刷新令牌,可参考令牌服务中的刷新/失效逻辑
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L101-L129)
- [user_service.go](file://internal/service/user_service.go#L203-L226)
- [response.go](file://internal/model/response.go#L20-L53)
## 结论
/api/v1/auth/login 提供了完整的用户名或邮箱登录能力具备参数校验、密码校验、JWT签发与登录日志记录。当前实现聚焦于登录流程本身后续可在速率限制、验证码、刷新令牌等方面进一步增强安全性与可用性。
## 附录
### API定义与示例
- 端点
- 方法POST
- 路径:/api/v1/auth/login
- 请求体
- username字符串必填支持用户名或邮箱
- password字符串必填长度6-128
- 成功响应
- code200
- data包含 token 与 userInfo
- 示例(结构示意)
- data: { token: "...", userInfo: { id, username, email, avatar, points, role, status, last_login_at, created_at, updated_at } }
- 失败响应
- 400请求参数错误
- 401未授权用户名/邮箱或密码错误、账号被禁用)
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L86-L147)
- [common.go](file://internal/types/common.go#L27-L31)
- [common.go](file://internal/types/common.go#L107-L126)
- [response.go](file://internal/model/response.go#L20-L53)
### JWT声明与令牌生命周期
- 声明内容
- user_id、username、role
- 过期时间、签发时间、生效时间、签发者等注册声明
- 令牌类型
- 当前登录返回访问令牌;刷新/失效等能力可参考令牌服务
章节来源
- [jwt.go](file://pkg/auth/jwt.go#L24-L53)
- [token_service.go](file://internal/service/token_service.go#L151-L238)