# 验证码API
**本文引用的文件**
- [routes.go](file://internal/handler/routes.go)
- [captcha_handler.go](file://internal/handler/captcha_handler.go)
- [captcha_service.go](file://internal/service/captcha_service.go)
- [redis.go](file://pkg/redis/redis.go)
- [response.go](file://internal/model/response.go)
- [auth_handler.go](file://internal/handler/auth_handler.go)
- [captcha_handler_test.go](file://internal/handler/captcha_handler_test.go)
- [captcha_service_test.go](file://internal/service/captcha_service_test.go)
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向验证码服务API的使用者与维护者,围绕路由组“/api/v1/captcha”下的两个核心端点:
- GET /generate:生成图形验证码(滑块拼图),返回主图、滑块图、验证码唯一标识及目标Y坐标等信息。
- POST /verify:验证用户提交的滑动偏移量,判断是否在容差范围内,并在验证通过后清理缓存。
文档将详细说明验证码的生成机制(基于滑块拼图算法)、有效期管理(Redis过期策略)、验证流程与状态处理,并给出请求/响应示例。同时,说明该服务如何与注册、登录、更换邮箱等功能集成以提升安全性。
## 项目结构
验证码API位于路由组“/api/v1/captcha”,由处理器与服务层共同实现,底层依赖Redis进行验证码数据的临时存储与过期控制。
```mermaid
graph TB
subgraph "路由层"
R["routes.go
注册 /api/v1/captcha/* 路由"]
end
subgraph "处理器层"
GH["captcha_handler.go
Generate/Verify"]
end
subgraph "服务层"
CS["captcha_service.go
GenerateCaptchaData/VerifyCaptchaData"]
end
subgraph "基础设施"
RC["redis.go
Client 封装"]
end
R --> GH
GH --> CS
CS --> RC
```
图表来源
- [routes.go](file://internal/handler/routes.go#L80-L86)
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L1-L77)
- [captcha_service.go](file://internal/service/captcha_service.go#L1-L166)
- [redis.go](file://pkg/redis/redis.go#L1-L175)
章节来源
- [routes.go](file://internal/handler/routes.go#L80-L86)
## 核心组件
- 路由注册:在路由组“/api/v1/captcha”下注册“/generate”和“/verify”两个端点。
- 处理器:负责参数绑定、调用服务层、组织响应。
- 服务层:负责验证码生成(滑块拼图)、目标坐标提取、Redis存储与过期设置、验证偏移量、容差判断、验证后清理。
- Redis客户端:提供Set/Get/Del等基础能力,封装错误处理。
章节来源
- [routes.go](file://internal/handler/routes.go#L80-L86)
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L1-L77)
- [captcha_service.go](file://internal/service/captcha_service.go#L1-L166)
- [redis.go](file://pkg/redis/redis.go#L1-L175)
## 架构总览
验证码API采用“路由 -> 处理器 -> 服务 -> Redis”的分层设计。生成阶段将目标坐标与容差信息写入Redis并设置过期时间;验证阶段读取目标坐标,计算容差并判定有效性,通过后立即删除对应键,防止重复使用。
```mermaid
sequenceDiagram
participant C as "客户端"
participant G as "Generate 处理器"
participant S as "验证码服务"
participant R as "Redis 客户端"
C->>G : GET /api/v1/captcha/generate
G->>S : GenerateCaptchaData(ctx, redisClient)
S->>S : 生成滑块拼图数据
S->>R : Set(key=captcha : id, value={tx,ty}, expire=5m)
S-->>G : 返回 masterImage, tileImage, captchaId, y
G-->>C : JSON {code,data{masterImage,tileImage,captchaId,y}}
```
图表来源
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L12-L34)
- [captcha_service.go](file://internal/service/captcha_service.go#L76-L135)
- [redis.go](file://pkg/redis/redis.go#L60-L83)
章节来源
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L12-L34)
- [captcha_service.go](file://internal/service/captcha_service.go#L76-L135)
## 详细组件分析
### 路由与端点
- 路由组“/api/v1/captcha”
- GET /generate:生成验证码数据(主图、滑块图、验证码ID、目标Y坐标)。
- POST /verify:验证用户提交的滑动偏移量。
章节来源
- [routes.go](file://internal/handler/routes.go#L80-L86)
### 生成验证码(GET /generate)
- 输入:无显式请求体。
- 输出:包含以下字段的数据对象
- masterImage:主图(base64字符串)
- tileImage:滑块图(base64字符串)
- captchaId:验证码唯一标识(用于后续验证)
- y:目标Y坐标(前端可据此定位滑块初始位置)
- 内部流程要点
- 生成唯一ID作为验证码会话标识。
- 生成滑块拼图数据,提取目标X/Y坐标。
- 将目标坐标序列化后写入Redis,键名前缀为“captcha:”,过期时间为300秒(5分钟)。
- 返回主图、滑块图、captchaId与修正后的Y坐标给前端。
请求/响应示例(路径)
- 请求:GET /api/v1/captcha/generate
- 响应:JSON
- code:200
- data.masterImage:主图(base64)
- data.tileImage:滑块图(base64)
- data.captchaId:验证码ID
- data.y:目标Y坐标
章节来源
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L12-L34)
- [captcha_service.go](file://internal/service/captcha_service.go#L76-L135)
### 验证验证码(POST /verify)
- 请求体字段
- captchaId:必填,验证码唯一标识
- dx:必填,用户滑动的X轴偏移量
- 验证逻辑
- 从Redis读取目标坐标(tx, ty)。
- 使用容差值(默认±3像素)判断 dx 是否在 [tx-3, tx+3] 范围内。
- 若通过:删除Redis中的验证码记录,返回成功;若失败:返回失败。
- 错误处理
- 参数缺失或格式错误:返回400。
- Redis查询失败或键不存在:返回500或提示“验证码已过期或无效”。
请求/响应示例(路径)
- 请求:POST /api/v1/captcha/generate
- JSON:{ "captchaId": "...", "dx": 123 }
- 响应:
- 成功:JSON { "code": 200, "msg": "验证成功" }
- 失败:JSON { "code": 400, "msg": "验证失败,请重试" }
章节来源
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L36-L76)
- [captcha_service.go](file://internal/service/captcha_service.go#L137-L166)
### 验证流程与容差机制
```mermaid
flowchart TD
Start(["开始"]) --> Read["读取Redis中的目标坐标(tx, ty)"]
Read --> Found{"键是否存在?"}
Found --> |否| Expired["返回错误:验证码已过期或无效"]
Found --> |是| Calc["计算 |dx - tx|"]
Calc --> Check{"是否 ≤ 容差(3)?"}
Check --> |是| Delete["删除Redis键"]
Delete --> Pass["返回验证成功"]
Check --> |否| Fail["返回验证失败"]
Expired --> End(["结束"])
Pass --> End
Fail --> End
```
图表来源
- [captcha_service.go](file://internal/service/captcha_service.go#L137-L166)
章节来源
- [captcha_service.go](file://internal/service/captcha_service.go#L137-L166)
### 与注册/登录/更换邮箱的安全集成
- 注册(/api/v1/auth/register)
- 在注册流程中,前端需先获取图形验证码并完成验证,再提交邮箱验证码(通过 /api/v1/auth/send-code 发送)。
- 后端在注册接口处验证邮箱验证码的有效性,确保注册过程安全。
- 登录(/api/v1/auth/login)
- 登录流程通常不强制要求图形验证码,但可在高风险场景(如异常IP、频繁尝试)引入图形验证码作为二次防护。
- 更换邮箱(/api/v1/user/change-email)
- 更换邮箱时,建议增加图形验证码与邮箱验证码双重校验,降低账户被恶意修改的风险。
章节来源
- [auth_handler.go](file://internal/handler/auth_handler.go#L17-L84)
- [auth_handler.go](file://internal/handler/auth_handler.go#L149-L192)
## 依赖关系分析
- 路由层依赖处理器层暴露的处理函数。
- 处理器层依赖服务层提供的生成与验证方法。
- 服务层依赖Redis客户端进行数据持久化与过期控制。
- 通用响应模型用于统一返回格式(尽管验证码端点未直接使用该模型,但整体风格一致)。
```mermaid
graph LR
Routes["routes.go"] --> Handler["captcha_handler.go"]
Handler --> Service["captcha_service.go"]
Service --> Redis["redis.go"]
Handler --> Model["response.go"]
```
图表来源
- [routes.go](file://internal/handler/routes.go#L80-L86)
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L1-L77)
- [captcha_service.go](file://internal/service/captcha_service.go#L1-L166)
- [redis.go](file://pkg/redis/redis.go#L1-L175)
- [response.go](file://internal/model/response.go#L1-L86)
章节来源
- [routes.go](file://internal/handler/routes.go#L80-L86)
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L1-L77)
- [captcha_service.go](file://internal/service/captcha_service.go#L1-L166)
- [redis.go](file://pkg/redis/redis.go#L1-L175)
- [response.go](file://internal/model/response.go#L1-L86)
## 性能考虑
- 生成阶段
- 滑块拼图生成与base64编码在单次请求中完成,建议在前端缓存主图/滑块图以减少重复生成开销。
- 验证阶段
- Redis读取与删除均为O(1),验证逻辑简单,延迟低。
- 过期策略
- 默认5分钟过期,避免长期占用内存;过期后自动失效,无需手动清理。
- 并发与重复使用
- 验证通过后立即删除Redis键,防止同一验证码被重复使用。
章节来源
- [captcha_service.go](file://internal/service/captcha_service.go#L123-L135)
- [captcha_service.go](file://internal/service/captcha_service.go#L157-L166)
## 故障排查指南
- 生成失败
- 现象:返回500,消息包含“生成验证码失败”。
- 可能原因:滑块拼图生成异常、Redis写入失败。
- 排查步骤:检查Redis连接状态、确认滑块拼图资源可用。
- 验证失败
- 现象:返回500或400,消息提示“验证码已过期或无效”或“验证失败,请重试”。
- 可能原因:captchaId无效或已过期、dx不在容差范围内、Redis读取异常。
- 排查步骤:确认captchaId正确、dx传值合理、Redis键存在且未过期。
- 参数错误
- 现象:返回400,消息提示“参数错误”。
- 可能原因:缺少captchaId或dx、JSON格式不正确。
- 排查步骤:检查请求体字段与类型。
章节来源
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L12-L34)
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L36-L76)
- [captcha_service.go](file://internal/service/captcha_service.go#L137-L166)
## 结论
验证码API通过滑块拼图与Redis过期机制,提供了轻量级、易部署的人机验证方案。其与注册、登录、更换邮箱等关键流程结合,可显著提升账户安全。建议在高风险场景引入图形验证码作为附加防护,并优化前端缓存策略以提升用户体验。
## 附录
### 请求/响应示例(路径)
- 获取验证码
- 请求:GET /api/v1/captcha/generate
- 响应:JSON { "code": 200, "data": { "masterImage": "...", "tileImage": "...", "captchaId": "...", "y": 123 } }
- 验证验证码
- 请求:POST /api/v1/captcha/verify
- JSON:{ "captchaId": "...", "dx": 123 }
- 响应:
- 成功:JSON { "code": 200, "msg": "验证成功" }
- 失败:JSON { "code": 400, "msg": "验证失败,请重试" }
章节来源
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L12-L34)
- [captcha_handler.go](file://internal/handler/captcha_handler.go#L36-L76)
### 单元测试要点(路径)
- 验证码生成与过期时间
- 测试:TestGenerateCaptchaData_ExpireTime
- 断言:过期时间为300秒(5分钟)
- 验证码验证逻辑
- 测试:TestVerifyCaptchaData_Logic
- 断言:在容差范围内(±3)返回成功,超出范围返回失败
- Redis键生成
- 测试:TestVerifyCaptchaData_RedisKey
- 断言:键名为“captcha:{id}”
- 处理器响应格式与错误处理
- 测试:TestCaptchaHandler_ResponseFormat、TestCaptchaHandler_ErrorHandling
- 断言:成功/失败响应格式与HTTP状态码
章节来源
- [captcha_service_test.go](file://internal/service/captcha_service_test.go#L1-L175)
- [captcha_handler_test.go](file://internal/handler/captcha_handler_test.go#L1-L134)