294 lines
12 KiB
Markdown
294 lines
12 KiB
Markdown
# 验证码API
|
||
|
||
<cite>
|
||
**本文引用的文件**
|
||
- [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)
|
||
</cite>
|
||
|
||
## 目录
|
||
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<br/>注册 /api/v1/captcha/* 路由"]
|
||
end
|
||
subgraph "处理器层"
|
||
GH["captcha_handler.go<br/>Generate/Verify"]
|
||
end
|
||
subgraph "服务层"
|
||
CS["captcha_service.go<br/>GenerateCaptchaData/VerifyCaptchaData"]
|
||
end
|
||
subgraph "基础设施"
|
||
RC["redis.go<br/>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) |