必须组合使用Token验证+PRG重定向+缓存控制,缺一不可;需开启token_on、启用Session、前后端Token名称一致、校验后立即redirect跳转新页面,AJAX须通过X-CSRF-TOKEN头传递并手动指定校验名。

在ThinkPHP 6.0中实现表单防重复提交,必须让前端隐藏域的Token值与后端Session中存储的Token严格匹配且一次性生效——只要页面缓存、多标签页、AJAX头缺失或redirect位置错误,整个机制就会失效。
开启并配置Token验证
打开config/app.php,找到'middleware'数组,在其中加入\think\middleware\TokenCheck::class中间件(TP6.0默认未启用该中间件)。
在config/app.php中设置以下三项:
【'token_on' => true】 —— 必须设为true,否则{:token()}不生成、$request->checkToken()永远返回true;
'token_name' => '__token__' —— 保持默认即可,若修改需前后端同步;
'token_type' => 'sha256' —— 建议从默认md5升级为sha256,避免碰撞风险。
模板中插入Token字段
方法一:直接使用内置函数生成隐藏域
<input type="hidden" name="__token__" value="{:token()}">
方法二:一键输出完整字段(含name和value)
{:token_field()}
方法三:自定义名称与哈希方式(如需区分登录/支付表单)
{:token_field('__login_token__', 'sha1')} —— 此时控制器校验必须用$request->checkToken('__login_token__'),否则校验失败。
【注意:不能在已关闭Session的请求中调用{:token()},会抛出Session未启动异常】
控制器中完成校验与跳转
第一步:确保Session已启动。在控制器构造方法或方法开头加session()->start();,TP6.0默认不自动开启Session。
第二步:在接收POST数据前立即校验Token
$check = $request->checkToken();
第三步:校验失败必须中断流程,不可继续执行业务逻辑
if (false === $check) {<br> return json(['code'=>403, 'msg'=>'重复提交或令牌无效']);<br>}
第四步:校验通过后,必须用redirect()跳转到新URL,禁止留在原action
redirect('success/index?token='.uniqid()); —— 【跳转地址不能是当前URL(如'index'),否则用户点浏览器刷新会再次POST】
AJAX提交时的特殊处理
在HTML头部添加meta标签承载Token:
{:token_meta()} → 渲染为<meta name="csrf-token" content="a1b2c3...">
jQuery AJAX请求头必须显式携带:
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }
服务端校验需指定名称(因AJAX不走form自动绑定):
$request->checkToken('X-CSRF-TOKEN');
校验通过后,若需刷新Token供下一次AJAX使用,手动调用:
$request->buildToken();

















