# 5.3 验证码机制 :::tip 概述 图形验证码由 utility/captcha 生成:6 位字符(排除易混淆字符)、PNG 渲染(TTF 字体 + 干扰线/噪点)、Redis 缓存 300 秒、一次性校验。字体缺失自动降级 SVG 渲染。 ::: ## 生成流程(GET /api/captcha) ```text 1) 生成随机码:captcha.length 位(默认 6,范围 4~8), 字符集 captcha.chars(默认排除 0O、1lI 等易混淆字符) 2) 生成 uuid 作为缓存键 3) 缓存:easygoadmin:captcha:{uuid} = 验证码值,TTL captcha.expire(300s) 4) 渲染 PNG:TTF 字体(resource/static/fonts/Vera.ttf)+ 干扰线(默认 8 条) + 噪点(默认 500 个);字体缺失降级 SVG 5) 返回 {"key": uuid, "captcha": "data:image/png;base64,..."}(前端直接展示) ``` ## 校验流程(captcha.Verify) ```text Verify(ctx, key, code): 1) GET easygoadmin:captcha:{uuid}(命中即删除——一次性校验) 2) 未命中 → 校验失败(过期或已用过) 3) 命中 → 与提交值比对(大小写不敏感处理) 4) 配置 captcha.bypassCode 且提交值相等 → 免校验直接通过 (仅开发调试使用,生产环境必须留空) ``` ## 配置项(manifest/config/config.yaml) ```yaml captcha: expire: 300 # 有效期(秒) bypassCode: "618618" # 万能验证码:携带该值免校验,仅开发调试使用,生产留空 length: 6 # 字符数,取值范围 4 到 8 chars: "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" # 字符集,已排除易混淆字符 fontPath: "resource/static/fonts/Vera.ttf" # TTF 字体路径,缺失降级 SVG imageWidth: 160 # 图片宽度(字符放不下时自动加宽防裁剪) imageHeight: 50 # 图片高度 fontSize: 40 # 字体大小 noiseLines: 8 # 干扰线数量(越多越难识别) noiseDots: 500 # 噪点数量 ``` ## 使用场景 | 场景 | 行为 | |------|------| | 登录 | 提供 key 时**强制**校验(auth Login 第 1 步) | | 令牌兑换 | 认证入口,与登录同享 authLimit 严阈值 | | 开发调试 | code 填 bypassCode(默认 618618)免校验 | | 生产 | bypassCode 必须留空(发布检查清单项) | ## 安全要点 + **一次性**:命中即删,防重放(同一验证码不能二次使用) + **短 TTL**:300 秒过期,防拖库后长期有效 + **独立限流**:/api/captcha 属认证入口,受 authLimit=10/分钟/IP 约束(防刷图) + **缓存层**:经 utility/cache(Redis 优先、内存降级)——内存降级时验证码单实例内有效 ## 常见问题 | 症状 | 原因与处理 | |------|-----------| | 验证码图片不显示 | 后端未启动 / /api/captcha 被限流;字体缺失不影响(降级 SVG) | | 填对了也报验证码错误 | 已用过(一次性)或超 300s;重新获取 | | 前端一直验证码错误但后端日志正常 | 检查 key 是否与图片配套(每次生成 key 不同) | | 生产环境想跳过验证码 | 禁止配置 bypassCode;调试可在测试环境进行 |