ThinkPHP Token校验失败主因有四:JSON请求未解析参数致$_POST为空;中间件执行顺序不当使session未启用;全局中间件无法被路由except覆盖;token过期时间因时区或缓存策略不一致导致误判。

Token解析失败:think\facade\Request::token() 返回 false
不是 Token 本身无效,而是中间件没等它被正确初始化就去校验了。ThinkPHP 的 Request::token() 默认从 $_POST 或 $_GET 读取 __token__ 字段,但接口常走 JSON body,此时 $_POST 为空,函数直接返回 false,后续校验自然报错。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认请求体类型:如果是
Content-Type: application/json,必须手动从原始输入解析 Token,不能依赖默认行为 - 在中间件中改用
$request->post('__token__')前,先调用$request->parseParams()(否则 JSON 数据不会自动映射到post()) - 更稳妥的做法是统一从 header 传 Token,比如
Authorization: Bearer {token},避开表单字段耦合
中间件执行顺序导致 token 校验失效
ThinkPHP 的中间件链里,TokenCheck 类如果放在 SessionInit 或 CookieParse 之前,会因 session 未启动而无法读取服务端存储的 token 值,报“token mismatch”。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 检查
app/middleware.php中间件注册顺序,确保TokenCheck在SessionInit之后 - 若使用无状态 JWT,不要依赖 session 存储 token,直接在中间件里验证签名和有效期,避免 session 依赖
- 调试时可在中间件开头加
var_dump(session_status() === PHP_SESSION_ACTIVE)确认 session 是否已就绪
接口请求绕过 token 校验却仍报错
明明在路由定义里写了 ['middleware' => []] 或用 except 排除了某些方法,但依然触发 token 检查——常见原因是中间件注册在全局(app/middleware.php),而路由分组或控制器注解的排除逻辑没生效。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 全局中间件无法被路由级
except覆盖,必须在app/middleware.php中用条件判断跳过,例如:if (in_array($request->action(), ['login', 'captcha'])) return $next($request); - 检查是否误将中间件同时注册在全局和路由分组中,造成重复校验
- 注意大小写:ThinkPHP 的
$request->action()返回的是小写方法名,别拿Login去匹配
Token 过期时间与缓存策略不一致
前端拿到的 token 显示还有 2 小时有效,但接口 5 分钟后就报错“token expired”,问题往往出在服务端校验时用了本地时间,而 token 签发用的是 Redis 缓存时间戳,两者时区或精度不一致。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 统一用 UTC 时间生成和校验 token,避免服务器时区干扰
- Redis 存 token 时,用
SETEX设置精确过期秒数,而不是靠应用层判断时间差 - 前端传来的 token 若含时间戳字段(如
exp),务必用date_default_timezone_set('UTC')后再time()对比,否则本地时区会偏移
Token 机制本身不复杂,但 ThinkPHP 把表单校验、session、中间件、路由控制几层叠在一起,稍一错位就会在错误信息里看不到真实原因。最常被忽略的是:JSON 请求体不触发自动参数解析,以及全局中间件的排除逻辑根本不起作用。



















