ThinkPHP 6 表单令牌验证失败的核心原因是前后端 token 值不一致或未正确传递;需检查模板中 {:token()} 是否在 form 内正确输出、控制器是否启用 token 中间件、AJAX 是否手动传入 token 字段、避免缓存导致 Token 过期,并可通过比对 clientToken 与 serverToken 快速定位问题。

ThinkPHP 6 表单令牌验证失败,核心不是“没生成”,而是前后端 __token__ 值不一致或未正确传递。下面给出可直接复用的排查步骤和对应代码,覆盖模板、控制器、AJAX、缓存等关键环节。
一、确保模板中正确输出 Token 字段
必须在 <form></form> 内部调用 {:token()},且不能重复调用:
⚠️ 注意:
- 不要用 {:csrf_token()}(TP6 不识别,会报错);
- 不要放在 form 外,否则提交时不会携带;
- 查看页面源码,确认生成的是:<input type="hidden" name="__token__" value="abc123...">
二、控制器中显式启用 Token 校验中间件
TP6 默认不自动校验所有 POST,需手动绑定。推荐在控制器类顶部声明:
namespace app\controller;
<p>use think\Controller;</p><p>class FormController extends Controller
{
protected $middleware = [
'token' // 启用 TokenCheck 中间件
];</p><pre class="brush:php;toolbar:false;">public function save()
{
// 此处无需再手动 checkToken(),中间件已拦截
// 验证通过后才执行后续逻辑
return json(['code' => 1, 'msg' => '提交成功']);
}}
如只需对某方法启用,也可在路由定义中加:Route::post('save', 'FormController/save')->middleware('token');
三、AJAX 提交时手动传 Token(重点!)
fetch / axios 不会自动带上隐藏域,必须显式提取并写入请求体:
const form = document.getElementById('myForm');
const tokenInput = form.querySelector('input[name="__token__"]');
const token = tokenInput ? tokenInput.value : '';
<p>fetch('{:url("save")}', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
title: form.title.value,
<strong>token</strong>: token // ✅ 必须手动传,字段名严格为 <strong>token</strong>
})
})
.then(r => r.json())
.then(res => console.log(res));</p>
✅ 更健壮写法(防多表单冲突):
在 form 上加 data 属性:<form data-token="{:token()}"></form>,JS 中用 form.dataset.token 读取。
四、避免缓存导致 Token 陈旧
浏览器或 CDN 缓存旧页面,会导致表单里是过期 Token。在控制器方法开头加强制不缓存头:
return view();
同时建议前端禁用重复提交:
- 提交按钮点击后立即
btn.disabled = true - 成功/失败后统一重置(勿仅靠 success 回调)
- 避免用
form.reset(),它会清空隐藏域值;如需重置,请重新渲染整个表单或手动补 token
五、调试辅助:快速验证 Token 是否匹配
在控制器中临时加入比对逻辑(上线前删掉):
public function save()
{
$clientToken = input('__token__');
$serverToken = \think\facade\Session::get('think_token');
<pre class="brush:php;toolbar:false;">if (empty($clientToken)) {
return json(['code' => 0, 'msg' => '客户端未提交 __token__ 字段']);
}
if ($clientToken !== $serverToken) {
return json(['code' => 0, 'msg' => 'Token 不一致', 'client' => $clientToken, 'server' => $serverToken]);
}
// 继续业务逻辑...}
该代码能明确告诉你哪边出错——是前端没传、传错值,还是服务端 session 异常丢失。











