ThinkPHP8.1项目需通过JWT实现统一API鉴权,安装firebase/php-jwt:^6.4.0,配置secret、UTC时区,封装JwtService生成含iat/exp的token,编写JwtAuth中间件校验Bearer token并注入jwtUser,路由分组绑定且排除login等接口。

ThinkPHP8.1项目需对API接口实施统一、可靠的身份核验与访问控制,避免未授权调用导致数据泄露或越权操作,不能依赖控制器内零散判断。
安装并配置JWT扩展库
执行composer require firebase/php-jwt:^6.4.0安装适配PHP 8.1的稳定版本——v6.4.0兼容性最佳,高版本如v6.10+要求PHP ≥8.0但可能触发算法不兼容。
在config/jwt.php中定义配置:
return ['secret' => env('JWT_SECRET', 'dev_default_key_2026')];
【JWT_SECRET必须写入.env且禁止提交到Git】,开发环境可用随机字符串,生产环境须用openssl rand -base64 32生成并严格保管。
确认date_default_timezone_set('UTC')已在public/index.php顶部执行,否则exp校验因时区偏差必然失败。
封装JwtService服务类
新建app\common\service\JwtService.php,命名空间为app\common\service。
构造generateToken()方法:payload中必须包含iat(签发时间)、exp(过期时间),且exp = time() + 7200动态计算,禁止硬编码固定时间戳。
调用\Firebase\JWT\JWT::encode($payload, $key, 'HS256')生成token——严禁手动拼接header/payload/base64编码,全部交由官方库处理。
验证服务可用性:在测试控制器中执行class_exists('Firebase\JWT\JWT')返回true即完成。
编写鉴权中间件
创建app\middleware\JwtAuth.php,handle方法内按顺序执行:
① 从$request->header('authorization')提取Bearer token,格式不符(如无Bearer前缀、空值)直接返回401;
② 使用\Firebase\JWT\JWT::decode()解析,【必须显式传入算法数组['HS256'],防止none算法攻击】;
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
③ 手动校验$decoded->exp > time()和isset($decoded->iat),仅靠库默认校验不够;
④ 验证通过后,执行$request = $request->withAttribute('jwtUser', $decoded)注入用户上下文,供后续控制器读取;
⑤ 失败时抛出HttpException(401, 'Unauthorized')中断流程,禁止静默放行。
绑定中间件到路由分组
在app/route/app.php中定义API路由分组:
Route::group(['middleware' => 'jwt.auth'], function () {
Route::get('user/profile', 'User/profile');
Route::post('article/publish', 'Article/publish');
})->except(['login', 'refresh']);
【except只对当前分组生效,全局注册的中间件无法使用except跳过】,若误在app/middleware.php中注册则所有请求都会被拦截。
登录接口Route::post('login', 'Auth/login')必须独立于该分组之外定义,否则无法获取初始token。
权限校验集成(可选增强)
方法一:在JwtAuth中间件内扩展校验逻辑——从$decoded->permissions数组中检查in_array('article.delete', $decoded->permissions),适用于token已预埋权限标识的场景。
方法二:配合RBAC中间件链式调用——在app/middleware.php中将CheckAuth::class置于CheckPermission::class之前,确保用户对象已注入再做权限判断。
路由名校验优于路径拼接:$request->rule()->getName()获取精确路由标识,避免$request->controller().'/'.$request->action()因大小写、多应用前缀引发错配。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










