thinkphp8微信小程序jwt认证需解决登录态持久化、token安全透传、无状态鉴权与用户上下文注入四大问题,必须弃用session改用jwt,安装firebase/php-jwt:^6.4.0并配置.env密钥,禁用none算法,校验class_exists('firebasejwtjwt')确保加载成功。

ThinkPHP8微信小程序后端JWT认证实战【移动】需解决小程序登录态持久化、token安全透传、无状态鉴权与用户上下文注入四大问题,不能直接复用Web端Session逻辑,否则每次请求都查库、无法支持多端并发、退出后token仍有效。
安装适配PHP 8.1+的JWT库
执行composer require firebase/php-jwt:^6.4.0——v6.4.0是当前唯一稳定兼容PHP 8.1且规避none算法漏洞的版本;v7.x要求PHP ≥8.2,与多数小程序生产环境(PHP 8.0–8.1)不匹配。
安装后立即验证:php -r "var_dump(class_exists('Firebase\JWT\JWT'));"输出bool(true)即成功。
若报错Class 'FirebaseJWTJWT' not found,请确认composer autoload已生效,不要手动require_once。
配置密钥与UTC时区
在config/jwt.php中写入:
return ['secret' => env('JWT_SECRET', 'dev_default_key_2026')];
【JWT_SECRET必须写入.env且禁止提交到Git】——开发可用openssl rand -base64 32生成,生产环境密钥须由运维统一分发并轮换。
打开public/index.php,在下方第一行插入:date_default_timezone_set('UTC');——【缺失此行会导致exp校验必然失败】,因小程序前端时间戳按UTC生成,PHP若用本地时区解析会偏差数小时。
封装JwtService生成带小程序字段的Token
新建app/common/service/JwtService.php,命名空间为appcommonservice。
在generateToken()方法中,payload必须包含微信小程序特有字段:
$payload = [
'uid' => $user['id'],
'openid' => $user['wx_openid'], // 小程序唯一标识,用于后续消息推送
'unionid' => $user['wx_unionid'] ?: null, // 多平台打通必需
'iat' => time(),
'exp' => time() + 7200, // 2小时有效期,兼顾安全性与体验
'jti' => uniqid('', true), // 防重放攻击
];
调用FirebaseJWTJWT::encode($payload, config('jwt.secret'), 'HS256')生成token——严禁拼接base64字符串或手动构造header,全部交由官方库处理。
编写JwtAuth中间件拦截并注入用户
执行php think make:middleware JwtAuth创建中间件。
在handle()方法内按顺序执行:
① 提取Authorization头:$auth = $request->header('authorization');注意ThinkPHP内部统一转小写,必须用'authorization',不能写'Authorization'或'Bearer'。
② 正则提取token:preg_match('/Bearers+(.+)/i', $auth, $m) ? $token = $m[1] : $token = null;——【不能用explode(' ', $auth)[1],空格可能在前后导致越界】。
③ 解析并校验:$decoded = FirebaseJWTJWT::decode($token, new FirebaseJWTKey(config('jwt.secret'), 'HS256'), ['HS256']);——【必须显式传入算法数组['HS256'],防止none算法攻击】。
④ 手动双重校验过期与签发时间:if (!isset($decoded->exp) || !isset($decoded->iat) || $decoded->exp
⑤ 注入用户上下文:$request = $request->withAttribute('jwtUser', $decoded);后续控制器可通过$this->request->getAttribute('jwtUser')直接读取openid等字段。
配置Nginx透传Authorization头
小程序前端通过wx.request({ header: { 'Authorization': 'Bearer xxx' } })发送token,但Nginx默认过滤Authorization头。
在Nginx站点配置的location块内添加:
proxy_set_header Authorization $http_authorization;
若使用HTTPS反向代理,还需加:proxy_pass_request_headers on;
配置完执行nginx -t && nginx -s reload,否则中间件永远收不到token。
路由分组绑定中间件并排除登录接口
在app/route/app.php中定义API分组:
Route::group(['middleware' => 'jwt.auth'], function () {
Route::get('user/info', 'User/info');
Route::post('order/create', 'Order/create');
})->except(['login', 'refresh', 'captcha']);
->except()参数必须显式列出无需鉴权的接口,【漏写login将导致小程序首次登录失败】。
登录接口返回token后,小程序端应立即调用wx.setStorageSync('token', res.data.token)持久化存储。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











