token::check()通过后必须手动销毁,因其仅校验不自动清空;thinkphp防重需前端禁用按钮、后端销毁token、提交后重定向三者协同,缺一不可。

Token::check() 验证失败是防重复提交最直接的信号,但它本身不是“事件”,而是表单生命周期里的一次性校验动作。ThinkPHP 的防重提交必须靠「前端禁用 + 后端 Token 销毁 + 重定向」三者咬合,缺一不可。
为什么 Token::check() 通过后还要手动销毁?
ThinkPHP 的 Token::check() 默认只做比对,不自动清空 session 中的 token。如果不手动调用 Token::reset() 或 session('token', null),同一个 token 就能被反复验证成功。
-
Token::check()返回true只代表“当前值匹配”,不代表“已消费” - 常见错误是验证完就跳转或返回,没清 token,导致刷新 POST 页面仍能通过
- 新版 ThinkPHP6+ 推荐用
Token::validate()替代,它内部自动 reset,但需确认配置中'token_on' => true已启用
表单页渲染时漏掉 {__token__/} 会怎样?
模板里没写 {__token__/},后端 Token::check() 必然失败,但表现可能出人意料:
- 开发环境常报
TokenException,页面中断;生产环境若未捕获,直接 500 - 部分版本会静默跳过验证(取决于
'token_on'和'token_name'配置),等于完全失效 - 注意:V6.3+ 默认 token 字段名是
_token,不是旧版的__token__,模板写错名也等同于没传
重定向没做对,用户一刷新就重复提交
POST 处理成功后必须用 redirect() 跳出当前 action,否则用户 F5 刷新,浏览器会重发原始 POST 请求——而此时 token 已销毁,Token::check() 必然失败,但业务逻辑可能已被执行两遍。
- 错误写法:
return $this->success('ok');—— 这仍是当前 action 的响应,没跳出 POST 生命周期 - 正确写法:
return redirect()->to('/success')->with('msg', '提交成功'); - 如果非要留在当前页(比如编辑页保存后继续改),必须重新生成新 token 并刷新整个表单 HTML,不能复用旧 DOM
接口场景下别把 Token::check() 当幂等控制
Web 表单和 API 是两套逻辑。Token::check() 依赖 session,而接口常无 session(尤其跨域、App 调用),强行塞进去会导致:
- 移动端重试时 token 已过期,返回 400 却不告诉用户“已成功”,体验断裂
- 支付回调等服务端主动请求,根本不会带 token,直接拦截
- 真正要的是幂等:用
Cache::remember('idempotent_' . $key, 120, function(){})做请求指纹锁,key 排除时间戳/随机数等动态字段
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











