Token校验必须放在中间件,不能在控制器中手写;需统一解析Authorization头、剥离Bearer前缀、绑定用户并校验状态与scope,Redis宜用白名单+TTL而非黑名单。

Token校验该放在中间件还是控制器里
必须放中间件,不能在每个接口里手写校验逻辑。越权防护是横切关注点,放控制器会导致漏校验、重复代码、Token刷新难统一。
- 中间件能拦截所有请求(包括未登录路由),控制器只能守自己那一块
- ThinkPHP 的
AuthMiddleware或自定义中间件可统一解析Authorization请求头 - 若放控制器,新增接口时极易忘记加
checkToken(),线上就成越权入口
Authorization头格式和解析常见错误
前端传的 Token 格式不对,后端解析就会直接失败,返回 401 却查不出原因。
- 标准格式是
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...,不是token: xxx或纯xxx - ThinkPHP 中用
$request->header('authorization')获取,注意大小写兼容(Nginx 默认会把 header 转成小写) - 没做前缀剥离就直接验签:错误写法
JWT::decode($token, $key, ['HS256'])→ 正确应先str_replace('Bearer ', '', $token)
Token 解析后如何绑定用户并防越权
解出 payload 只是第一步,关键在验证「这个 token 真的属于当前请求的用户」,否则照样越权。
- payload 里的
uid必须和数据库中该用户的last_login_token_hash匹配(用于支持主动踢下线) - 禁止仅靠
uid就赋值$this->user = User::find($uid),要查库确认状态:status = 1且is_deleted = 0 - 敏感操作(如删订单、改密码)需额外校验
scope字段,比如order:write,不能只认user:basic
Redis 存 token 黑白名单的坑
想用 Redis 记录失效 Token?小心性能反被拖垮,或根本没生效。
立即学习“PHP免费学习笔记(深入)”;
- 黑名单模式(存已退出的 token)对高并发不友好:每次请求都得查一次 Redis,网络延迟放大 10ms+,压测易雪崩
- 更稳妥的是「白名单 + 过期时间」:Redis key 设为
token:{hash},value 存uid和scope,TTL 严格等于 JWT 的exp - 别用
setex手动算过期秒数,ThinkPHP 的Cache::set()自带 TTL,但要注意时区:exp是 Unix 时间戳,不是秒偏移
越权问题最麻烦的不是校验逻辑写不出来,而是校验之后没真正切断上下文——比如中间件里验了 token,但控制器里又用 input('user_id') 去查数据,这就等于把门锁了却把钥匙塞给攻击者。



















