根本原因是session在跨域、移动端及api调用中天然不可靠,必须改用带唯一token的缓存机制替代;think-captcha的$api=true分支已支持返回code+base64图片,只需将验证码存入redis/file缓存并绑定token,前端携带token校验,统一转小写、去空格、设过期时间并及时删除。

前后端分离下验证码失效,根本原因不是代码写错了,而是存储和验证机制没适配无状态场景。Session 在跨域、移动端或 API 调用中天然不可靠,强行依赖它只会反复踩坑。
改用缓存替代 Session 存储验证码
Think-Captcha 默认走 Session,但它的 create() 方法已预留 $api = true 分支,返回的是纯数据而非图片输出。关键是要把验证码文本存到缓存(如 Redis 或 File),而不是 Session。
- 修改
vendor/topthink/think-captcha/src/Captcha.php中的generate()方法:把$this->session->set('captcha', [...])替换为Cache::set('captcha_' . $token, $hash, 300)($token可用随机字符串或前端传入的唯一标识) - 在
check()方法里,对应改成Cache::get('captcha_' . $token)读取,校验通过后立即Cache::delete() - 避免全局 key(如
'captcha'),必须绑定唯一标识,防止多用户/多请求冲突
前端需主动管理 token 并传递校验参数
验证码图片接口应返回一个临时 token,前端把它存在内存或表单 hidden 字段里,提交时一并带上。不能指望浏览器自动带 Cookie。
- 后端生成验证码时,返回结构类似:
{ "token": "abc123", "img": "data:image/png;base64,..." } - 前端请求校验接口时,POST 数据包含
token和用户输入的code - 后端校验逻辑根据该
token查缓存,不依赖任何会话上下文
注意大小写、空格与过期控制
验证码失败 80% 是细节问题,不是架构问题。
- 比对前统一转小写:
mb_strtolower($input, 'UTF-8'),避免用户输错大小写 - 去除首尾空格:
trim($input),防止粘贴带空格 - 缓存设置明确过期时间(建议 120–300 秒),避免旧码长期有效
- 校验成功后立刻删除缓存,防止重复使用
排查常见干扰项
即使逻辑正确,环境问题也会导致“看似失效”。
- 确认 GD 库已启用(
phpinfo()查gd支持) - 检查
runtime/cache/目录是否可写(File 缓存驱动下) - 前端 img src 加时间戳防缓存:
src="/captcha?_t=" + Date.now() - 若用 Redis 缓存,确认连接正常、数据库选择正确
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











