ThinkPHP + JWT 实现 Token 认证(API 安全)

ThinkPHP + JWT 实现 Token 认证(API 安全)
最新回答
看不见的坚强

2023-05-01 16:53:28

在ThinkPHP中结合JWT实现Token认证的核心步骤包括生成JWT、创建验证中间件、扩展Payload信息、处理错误及优化实践,以下为具体实现方案

一、JWT生成与基础配置
  1. 安装依赖库通过Composer安装ThinkPHP的JWT扩展包:

    composer require firebase/php-jwt
  2. 生成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];
二、创建JWT验证中间件
  1. 中间件逻辑在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); }}
  2. 注册中间件在app/middleware.php中全局注册或按路由注册:

    return [ // 全局中间件 appmiddlewareJwtAuth::class,];
三、扩展Payload实蠢枣现细粒度控制
  1. 包含角色与权限在生并档答成Token时加入权限字段,用于后续中间件或控制器中的权限校验:

    $user = ['id' => 1, 'roles' => ['admin'], 'permissions' => ['user:edit']];$payload = [ // ...其他标准字段 'roles' => $user['绝慧roles'], 'permissions' => $user['permissions']];
  2. 权限校验中间件示例创建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');
四、常见错误处理与调试
  1. 错误类型与响应

    Token未提供:检查请求头是否包含Authorization: Bearer <token>。

    Token过期:设置合理的exp时间,前端捕获401错误后跳转登录或刷新Token。

    签名无效:确保服务器与客户端使用相同的密钥(jwt_secret)。

  2. 调试工具推荐

    JWT.io:在线解码验证Token结构。

    Postman:测试接口时手动添加Authorization头。

    日志记录:在中间件中记录异常信息:try { // ...解码逻辑} catch (Exception $e) { thinkfacadeLog::error('JWT验证失败: ' . $e->getMessage()); return json(['code' => 401, 'msg' => '认证失败']);}

五、性能优化与最佳实践
  1. 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' => '刷新失败']); }}

  2. 安全存储与传输

    客户端存储:优先使用HttpOnly Cookie(需配合CSRF保护)或localStorage(需防范XSS)。

    HTTPS传输:强制使用HTTPS防止Token被窃取。

  3. 密钥管理

    定期更换jwt_secret(需处理旧Token兼容性)。

    环境变量存储密钥(如.env文件):JWT_SECRET=your_random_string_here

  4. Payload优化

    避免存储敏感信息(如密码),仅保留必要字段。

    控制Payload大小(建议不超过2KB),减少网络开销。

通过以上步骤,可在ThinkPHP中构建一个安全、高效的JWT认证系统,兼顾无状态特性与细粒度权限控制。