ThinkPHP防CSRF需生成、传输、校验闭环:必须显式启用token验证(路由/中间件/手动checkToken),{:token()}唯一合法写法且须在form内单次使用,AJAX需主动携带,失败后需重刷token。

ThinkPHP 防 CSRF 的核心不是“加个隐藏域就完事”,而是生成、传输、校验三者闭环。只写 {:token()} 不开启验证,等于没设防;开了验证但没传对字段或没配对方法,照样报 TokenMismatchException。
必须开启 token 校验机制
ThinkPHP 默认不自动拦截所有 POST 请求。token 验证需显式启用,否则表单里的 __token__ 字段只是摆设。
- 在路由定义中加
['token' => true]:Route::post('user/update', 'User/update')->middleware(TokenCheck::class); - 在控制器里声明中间件:
protected $middleware = [TokenCheck::class]; - 或手动校验:
$request->checkToken(),支持传入字段名(如'_token')和数据数组
表单隐藏域要写对位置和方式
{:token()} 是唯一推荐的模板写法({:csrf_token()} 会报错)。它自动生成 <input type="hidden" name="__token__" value="xxx">,但有硬性要求:
- 必须放在
<form>标签内部,且只能出现一次 - 不能用 JS 动态拼接或重复调用,否则多个同名字段会导致校验失败
- 字段名要和后端配置一致——默认是
__token__,若改了(如用token_field('_hash_', 'md5')),路由/控制器校验时也得同步指定
POST 后 token 失效?页面回显要重刷
校验通过或失败后,token 默认作废。用户填错表单被退回原页再提交,必然失败。
立即学习“PHP免费学习笔记(深入)”;
- 控制器中渲染错误页前,调用
TokenService::reset()(TP6.1+)或TokenService::buildToken(true)强制刷新 - 模板仍用
{:token()},它会自动读取最新值 - 别在 JS 里提前缓存
input[name="__token__"].value,那会锁死旧 token
AJAX 提交必须带 token
表单自动注入不管用,AJAX 请求需主动携带:
- 推荐方案:页面
<head>插入<meta name="csrf-token" content="{:token()}">,JS 全局设置:$.ajaxSetup({headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}}); - 也可把 token 放请求体(
data: {__token__: 'xxx', ...}),但需确保后端checkToken()读的是同一来源 - 动态加载的表单(如弹窗内),不能依赖模板函数,要用
Url::build('token')或Request::token()动态获取



















