2023-05-01 16:53:28
在ThinkPHP中结合JWT实现Token认证的核心步骤包括生成JWT、创建验证中间件、扩展Payload信息、处理错误及优化实践,以下为具体实现方案:
一、JWT生成与基础配置安装依赖库通过Composer安装ThinkPHP的JWT扩展包:
composer require firebase/php-jwt生成JWT示例在用户登录成功后生成包含用户信息的Token:
use FirebaseJWTJWT;$user = ['id' => 1, 'username' => 'test'];$payload = [ 'iss' => 'your_domain', // 签发者 'aud' => 'your_api', // 接收方 'iat' => time(), // 签发时间 'exp' => time() + 3600, // 过期时间(1小时) 'sub' => $user['id'], // 用户ID 'data' => $user // 自定义用户数据];$key = config('app.jwt_secret'); // 从配置读取密钥$jwt = JWT::encode($payload, $key, 'HS256');return ['token' => $jwt];中间件逻辑在app/middleware目录下创建JwtAuth.php,实现请求头Token验证:
namespace appmiddleware;use Closure;use thinkRequest;use FirebaseJWTJWT;use FirebaseJWTKey;use FirebaseJWTExpiredException;class JwtAuth{ public function handle(Request $request, Closure $next) { $token = $request->header('Authorization'); if (!$token) { return json(['code' => 401, 'msg' => 'Token未提供']); } try { $decoded = JWT::decode($token, new Key(config('app.jwt_secret'), 'HS256')); $request->authUser = (array)$decoded->data; // 将用户信息挂载到请求对象 } catch (ExpiredException $e) { return json(['code' => 401, 'msg' => 'Token已过期']); } catch (Exception $e) { return json(['code' => 401, 'msg' => 'Token无效']); } return $next($request); }}注册中间件在app/middleware.php中全局注册或按路由注册:
return [ // 全局中间件 appmiddlewareJwtAuth::class,];包含角色与权限在生并档答成Token时加入权限字段,用于后续中间件或控制器中的权限校验:
$user = ['id' => 1, 'roles' => ['admin'], 'permissions' => ['user:edit']];$payload = [ // ...其他标准字段 'roles' => $user['绝慧roles'], 'permissions' => $user['permissions']];权限校验中间件示例创建CheckPermission.php中间件,结合JWT中的权限字段实现控制:
namespace appmiddleware;use Closure;class CheckPermission{ public function handle($request, Closure $next, $permission) { $userPermissions = $request->authUser['permissions'] ?? []; if (!in_array($permission, $userPermissions)) { return json(['code' => 403, 'msg' => '无权限访问']); } return $next($request); }}路由中使用:
Route::get('user/edit', 'User/edit')->middleware(appmiddlewareCheckPermission::class, 'user:edit');错误类型与响应
Token未提供:检查请求头是否包含Authorization: Bearer <token>。
Token过期:设置合理的exp时间,前端捕获401错误后跳转登录或刷新Token。
签名无效:确保服务器与客户端使用相同的密钥(jwt_secret)。
调试工具推荐
JWT.io:在线解码验证Token结构。
Postman:测试接口时手动添加Authorization头。
日志记录:在中间件中记录异常信息:try { // ...解码逻辑} catch (Exception $e) { thinkfacadeLog::error('JWT验证失败: ' . $e->getMessage()); return json(['code' => 401, 'msg' => '认证失败']);}
Token刷新机制
生成Token时返回access_token和refresh_token,后者过期时间更长。
前端在access_token过期时使用refresh_token请求新Token:// 刷新Token接口示例public function refresh(){ $refreshToken = input('refresh_token'); try { $decoded = JWT::decode($refreshToken, new Key(config('app.jwt_secret'), 'HS256')); $newAccessToken = JWT::encode([...], config('app.jwt_secret'), 'HS256'); return ['access_token' => $newAccessToken]; } catch (Exception $e) { return json(['code' => 401, 'msg' => '刷新失败']); }}
安全存储与传输
客户端存储:优先使用HttpOnly Cookie(需配合CSRF保护)或localStorage(需防范XSS)。
HTTPS传输:强制使用HTTPS防止Token被窃取。
密钥管理
定期更换jwt_secret(需处理旧Token兼容性)。
环境变量存储密钥(如.env文件):JWT_SECRET=your_random_string_here
Payload优化
避免存储敏感信息(如密码),仅保留必要字段。
控制Payload大小(建议不超过2KB),减少网络开销。
通过以上步骤,可在ThinkPHP中构建一个安全、高效的JWT认证系统,兼顾无状态特性与细粒度权限控制。