根本原因是跨域下Session失效导致验证码校验失败,需停用Session改用Redis存储验证码并配合唯一key传递:关闭useSession、移除Session中间件、清空runtime/session、配置Redis为验证码专用存储、生成时存小写code、校验时用hash_equals安全比对并立即删除key。

ThinkPHP8项目要做前后端分离,验证码总校验失败,刷新页面后Session丢失、跨域请求拿不到验证码值——根本原因是Session在跨域下失效,浏览器不自动携带Cookie,服务端取不到captcha.key。必须放弃Session绑定,改用Redis存储验证码并配合唯一ID传递。
停用Session验证码机制
打开config/captcha.php,将'useSession' => true改为false;同时确认'api' => true已启用。这一步强制Captcha::create()返回包含code和Base64图片的数组,跳过所有Session写入逻辑。
删除app/middleware.php中\think\middleware\SessionInit::class这一行。Session中间件必须移除,否则框架仍会初始化session_start(),干扰无状态流程。
【务必清空runtime/session/目录】——残留的file型Session文件可能被意外读取,导致captcha_check()误判为已存在旧值。
立即学习“PHP免费学习笔记(深入)”;
配置Redis作为验证码存储后端
打开config/cache.php,在'stores' => ['redis' => [...]]区块中,显式填写:'host' => '127.0.0.1'、'port' => 6379、'database' => 2(专用于验证码,与cache/session隔离)、'auth' => 'your_redis_password'(若Redis启ACL)。
在config/session.php中,确保'type' => 'redis'已被注释或设为'type' => 'none'。此时Session驱动完全关闭,避免任何隐式依赖。
运行命令php think optimize:clear清除配置缓存,让新Redis配置立即生效。
重写验证码生成接口
方法一:直接调用Captcha类生成
在控制器中新增方法:
PHP 8.5.12 RC 1 候选版修复 BZ2、DOM、PDO、Opcache/JIT 和扩展内存安全问题,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
public function getCaptcha() {<br> $captcha = new \think\captcha\Captcha();<br> $result = $captcha->create();<br> $key = 'captcha_' . uniqid('', true);<br> cache($key, strtolower($result['code']), 300);<br> return json(['key' => $key, 'img' => $result['img']]);<br>}
注意:必须对code调用strtolower(),因为前端输入通常为小写,而默认验证码含大小写字母;缓存时间设为300秒(5分钟),避免用户填错后反复刷新导致key堆积。
方法二:复用官方扩展但绕过Session
修改vendor/topthink/think-captcha/src/Captcha.php的create()方法,在if ($this->api)分支内,删掉$this->store($code)这一行——它原本就是往Session里写,现在必须彻底禁用。
实现无状态校验逻辑
第一步:从前端请求中提取关键参数
接收表单提交时,必须同时拿到两个字段:captcha(用户输入的文本)和captcha_key(前端隐藏域传来的key)。缺一不可,否则无法定位Redis中的记录。
第二步:从Redis读取并比对
执行$stored = cache($captcha_key);,若返回false说明key已过期或不存在,直接拒绝;若返回字符串,则用hash_equals($stored, strtolower($captcha))进行安全比对——不能用==,防止时序攻击。
第三步:校验后立即清理
比对成功后,立刻执行cache($captcha_key, null);或Cache::store('redis')->delete($captcha_key);。这一步不可省略,否则同一key可重复使用,失去防刷意义。


















